Search Unity

Question NotServerException: Only server can spawn NetworkObjects

Discussion in 'Netcode for GameObjects' started by XeLLLeR, Apr 9, 2022.

  1. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    I get this exception from the client. On the host everything is working well. So I have two scenes, in the first I'm trying to start host/client from RoomConnection script and if everything is ok I just load game scene. When second scene is loaded in Room script I spawn player through ServerRpc function. And then I get exceptions in the console.
    RoomConnection script:
    Code (CSharp):
    1. public void OnHostButtonClick()
    2.         {
    3.             RoomStats.Instance.ConnectedUserType = "Host";
    4.  
    5.             try
    6.             {
    7.                 NetworkManager.Singleton.StartHost();
    8.              
    9.                 SceneManager.LoadScene("Room");
    10.             }
    11.             catch (Exception e)
    12.             {
    13.                 print("Unable to host game!");
    14.              
    15.                 Console.WriteLine(e);
    16.                 throw;
    17.             }
    18.         }
    19.  
    20.         public async void OnJoinButtonClick()
    21.         {
    22.             RoomStats.Instance.ConnectedUserType = "Client";
    23.          
    24.             bool isClientConnectedSuccessfully = false;
    25.             NetworkManager.Singleton.OnClientConnectedCallback += obj => isClientConnectedSuccessfully = true;
    26.          
    27.             NetworkManager.Singleton.GetComponent<UNetTransport>().ConnectAddress = ipInputField.text;
    28.  
    29.             try
    30.             {
    31.                 NetworkManager.Singleton.StartClient();
    32.  
    33.                 await Task.Delay(5000);
    34.  
    35.                 if (isClientConnectedSuccessfully)
    36.                 {
    37.                     SceneManager.LoadScene("Room");
    38.                 }
    39.                 else
    40.                 {
    41.                     NetworkManager.Singleton.Shutdown();
    42.                     print("Client can not connect to the host!");
    43.                 }
    44.             }
    45.             catch (Exception e)
    46.             {
    47.                 Console.WriteLine(e);
    48.                 throw;
    49.             }
    50.         }
    Room script:

    Code (CSharp):
    1.         private void OnEnable()
    2.         {
    3.             _uNetTransport = NetworkManager.Singleton.gameObject.GetComponent<UNetTransport>();
    4.  
    5.             SceneManager.sceneLoaded += DisplayRoomIP;
    6.             SceneManager.sceneLoaded += (scene, mode) => SpawnPlayerServerRpc();
    7.         }
    8.  
    9.         private void DisplayRoomIP(Scene scene, LoadSceneMode mode)
    10.         {
    11.             string userType = RoomStats.Instance.ConnectedUserType;
    12.  
    13.             if (userType != "Host" && userType != "Client")
    14.             {
    15.                 throw new Exception("Who are you?!");
    16.             }
    17.  
    18.             roomIP.text = userType == "Host" ? GetLocalIPAddress() : _uNetTransport.ConnectAddress;
    19.         }
    20.  
    21.         [ServerRpc(RequireOwnership = false)]
    22.         private void SpawnPlayerServerRpc()
    23.         {
    24.             NetworkObject player = Instantiate(playerPrefab).GetComponent<NetworkObject>();
    25.             player.SpawnAsPlayerObject(NetworkManager.Singleton.LocalClientId);
    26.         }
    Screenshot 2022-04-09 085159.png Screenshot 2022-04-09 085218.png
     
    Last edited: Apr 9, 2022
  2. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    It looks like the RPC is being run on the client rather than the server, you won't get an indication of that until you hit something server specific. You could add a debug line at the start to be sure.

    Is Room derived from NetworkBehaviour?
     
  3. joanadcastro

    joanadcastro

    Joined:
    Apr 8, 2022
    Posts:
    3
    Think you can help me with my code?
     
  4. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    Hard to say, create a new thread stating the problem you're having.
     
    Mike0412 likes this.
  5. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    No, but I tried to add and it doesn't make difference.
     
  6. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    Does Room's NetworkObject component have a NetworkObjectId, you can check it in Unity editor at runtime.

    You could try updating Netcode to 1.0.0-pre.7 but it looks like you're using UNet transport and that's been removed in favour of the Unity transport so it will likely break your code.
     
  7. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    So I installed new Unity Transport and updated netcode package. And now I can't connect to the host for some reasons.I just I swapped UNetTransport to UnityTransport in the RoomConnection script. Client starts and IP address get right value, but can't connect to the host as I said.
     
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    You don't need to install the Unity transport, it's now incorporated in the Netcode package.

    I would create a new test project to sort out the connection and also to do some simple RPC testing then work out what's going on differently in your main project.
     
  9. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    Could it have something to do with loading a scene in a non-networked way? I.e. if you call
    NetworkManager.Singleton.SceneManager.LoadScene
    instead of
    SceneManager.LoadScene
    , does that solve it?
     
  10. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    I think it's not a problem for now. I start client and only after that I check for client has connected and then load scene. So it starts a client (I can track it in the inspector), but OnClientConnectedCallback doesn't awake for some reasons.
     
  11. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    You're calling LoadScene when you start the host when really as CosmoM says you should be using Netcode's SceneManager.LoadScene call.

    I think you're missing the benefit of the OnClientConnectedCallback by having it set a variable, if you use a function you can do away with having to wait a fixed amount of time before checking the variable is set. Also if you let the server's SceneManager handle the scene loading it will switch it to the Room scene automatically.

    For the RPC Issue, are you spawning Room at runtime or does it already exist in the Room scene? Did you check that the Room's NetworkObject component had a NetworkObjectId, the client's should match the host.

    If you want to make your project available I can take a look at it.
     
  12. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    So I created a new empty project to make all from the start. And I made scene loading through netcode's scene management. And now from the client I can't load a room scene because of NotServerException: Only server can start a scene event, but I'm using [ServerRpc] attribute. So probably I make it in the wrong way? Should I use another attribute to make action on the server?
    UPD: Yeah, Room object already exist in the scene. Screenshot 2022-04-10 094900.png Screenshot 2022-04-10 094921.png
     
    Last edited: Apr 10, 2022
  13. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    If you have this code for the host:

    Code (CSharp):
    1.      try
    2.             {
    3.                 NetworkManager.Singleton.StartHost();
    4.                
    5.                 NetworkManager.Singleton.SceneManager.LoadScene("Room", LoadSceneMode.Single);
    6.             }
    The client will switch to the Room scene on connection, you don't need the ServerRpc call.

    It's likely, if you're making scene changes yourself and not through server's SceneManager existing network objects won't be spawned, but again check the NetworkObjectId.
     
  14. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Client can't connect to the host for some reasons. Connection address gets right value
    Code (CSharp):
    1. public class RoomConnection : NetworkBehaviour
    2. {
    3.     [SerializeField] private TMP_InputField ipInputField;
    4.  
    5.     public void OnHostButtonClick()
    6.     {
    7.         RoomStats.Instance.ConnectedUserType = "Host";
    8.  
    9.         try
    10.         {
    11.             NetworkManager.Singleton.StartHost();
    12.          
    13.             NetworkManager.Singleton.SceneManager.LoadScene("Room", LoadSceneMode.Single);
    14.         }
    15.         catch (Exception e)
    16.         {
    17.             Console.WriteLine(e);
    18.             throw;
    19.         }
    20.     }
    21.  
    22.     public void OnJoinButtonClick()
    23.     {
    24.         RoomStats.Instance.ConnectedUserType = "Client";
    25.  
    26.         NetworkManager.Singleton.gameObject.GetComponent<UnityTransport>().ConnectionData.Address = ipInputField.text;
    27.  
    28.         NetworkManager.Singleton.OnClientConnectedCallback += obj => print("Yep");
    29.         NetworkManager.Singleton.StartClient();
    30.     }
    31. }
    UPD: RoomConnection object id = 0
    Room object id = 2
    Player (host) object id = 3
    Idk which object has id 1
     
  15. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    For testing remove any code you don't need, and/or use plenty of Debug output to check values are correct.

    For now use the default Unity transport values, in the editor it should look something like this. upload_2022-4-10_8-32-42.png
     
  16. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Yeah, everything works perfectly if I use default connection ip address.
     
  17. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    I don't know your development setup and why you're not using localhost but you might also need to set the Server Listen Address.
     
  18. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Server Listen Address doesn't work. Do you mean default ip under localhost?
     
  19. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    I've never had to use a separate Server Listen Address but as you're having connection problems I thought it worth a try.

    Yes I was wondering why you weren't using the 127.0.0.1 loopback address during development?
     
  20. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Haha, I don't even remember why I did this actually :) I guess it was something like password, because I tried to make connection approval by following Dapper Dino tutorial, but it didn't work. The byte array that I received on the callback was always equals to null. I tried to do this again right now, I thought maybe problem was in UNetTransport, but no. So maybe you can help me with this? Should I create a new thread? I tried to google this problem, but didn't find the answer.
     
  21. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    I've not done a lot with connection approval, I just use it as a way of getting the player name to the server.

    Code (CSharp):
    1.             NetworkManager.Singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes(PlayerPrefs.GetString("name"));
    In ConnectionApprovalCallback:
    Code (CSharp):
    1.        public void ConnectionApprovalCallback(byte[] connectionData, ulong clientId, NetworkManager.ConnectionApprovedDelegate callback)
    2.         {
    3.             Debug.Log("ConnectionApprovalCallback clientId: " + clientId + " name: " + Encoding.ASCII.GetString(connectionData));
    4.  
    5.             ServerClientManager clientManager = ServerClientsManager.Instance().InitialiseClient(clientId, Encoding.ASCII.GetString(connectionData));
    6.  
    7.             clientManager.ClientState = ClientState.Authenticated;
    8.  
    9.             callback(false, null, true, null, null);
    10.         }
     
  22. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    I see. I get null connection data for some reasons...
     
  23. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    I set connectionData just before StartClient() if it helps.
     
  24. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Yeah, that's the point, I do it too.
     
  25. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    Is connectionData null or an empty array? When not set it's an empty array. It might be worth reading back connectionData to check what you're assigning is what's expected.
     
  26. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    It returns an empty byte array.
    Code (CSharp):
    1.     public class RoomConnection : NetworkBehaviour
    2.     {
    3.         [SerializeField] private TMP_InputField passwordInputField;
    4.  
    5.         public void OnHostButtonClick()
    6.         {
    7.             RoomStats.Instance.ConnectedUserType = "Host";
    8.  
    9.             try
    10.             {
    11.                 NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck;
    12.                 NetworkManager.Singleton.StartHost();
    13.             }
    14.             catch (Exception e)
    15.             {
    16.                 print("Unable to host game!");
    17.                
    18.                 Console.WriteLine(e);
    19.                 throw;
    20.             }
    21.         }
    22.        
    23.         public void OnJoinButtonClick()
    24.         {
    25.             RoomStats.Instance.ConnectedUserType = "Client";
    26.  
    27.             try
    28.             {
    29.                 NetworkManager.Singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes(passwordInputField.text);
    30.                 print("first " + NetworkManager.Singleton.NetworkConfig.ConnectionData.Length);
    31.                 NetworkManager.Singleton.StartClient();
    32.             }
    33.             catch (Exception e)
    34.             {
    35.                 Console.WriteLine(e);
    36.                 throw;
    37.             }
    38.         }
    39.        
    40.         private void ApprovalCheck(byte[] connectionData, ulong userID, NetworkManager.ConnectionApprovedDelegate callback)
    41.         {
    42.             print("second " + connectionData.Length);
    43.             string password = Encoding.ASCII.GetString(connectionData);
    44.  
    45.             if (password == passwordInputField.text)
    46.             {
    47.                 NetworkManager.Singleton.SceneManager.LoadScene("Room", LoadSceneMode.Single);
    48.             }
    49.             else
    50.             {
    51.                 print("Bruh");
    52.             }
    53.         }
    54.     }
     
  27. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    I have something similar in test working fine, I'm out of ideas unfortunately.
     
  28. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    It's ok, anyway huge thank you!
     
    cerestorm likes this.
  29. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    It looks to me like the client is not subscribed to the approval callback, shouldn't it be? This works for me:

    Code (CSharp):
    1. [System.Serializable]
    2. public class ConnectionPayload {
    3.     public int playerTeam;
    4.     public string playerName;
    5.  
    6.     public ConnectionPayload(int team,string name) {
    7.         playerTeam=team;
    8.         playerName=name;
    9.     }
    10. }
    11.  
    12. public class Login : NetworkBehaviour {
    13.     private void OnEnable() {
    14.         NetworkManager.Singleton.ConnectionApprovalCallback+=ApprovalCheck;
    15.     }
    16.  
    17.     public void Join(int playerTeam,string playerName) {
    18.         string payload=JsonUtility.ToJson(new ConnectionPayload(playerteam,playerName));
    19.         byte[] payloadBytes=System.Text.Encoding.UTF8.GetBytes(payload);
    20.         NetworkManager.Singleton.NetworkConfig.ConnectionData=payloadBytes;
    21.         NetworkManager.Singleton.StartClient();
    22.     }
    23.  
    24.     private void ApprovalCheck(byte[] connectionData,ulong clientId,NetworkManager.ConnectionApprovedDelegate Callback) {
    25.         if (clientId!=0) { //Approval check is not called reliably for the host, but we have the host's data anyway, so skip it
    26.             ConnectionPayload payload=JsonUtility.FromJson<ConnectionPayload>(System.Text.Encoding.ASCII.GetString(connectionData));
    27.             //Store data somewhere
    28.         }
    29.         //Call Callback
    30.     }
    31. }
     
    cerestorm likes this.
  30. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    I'd say that is the problem. In pre7 I'm getting a warning if the client isn't subscribed.
     
  31. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    I tried to put subscription into the OnEnable method, but now it looks like approval callback never called.
    Also if put subscription before the start host I get this console warning.

    Code (CSharp):
    1. public class RoomConnection : NetworkBehaviour
    2. {
    3.     [SerializeField] private TMP_InputField passwordField;
    4.    
    5.     private void OnEnable()
    6.     {
    7.         NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck;
    8.     }
    9.  
    10.     public void OnHostButtonClick()
    11.     {
    12.         NetworkManager.Singleton.StartHost();
    13.     }
    14.    
    15.     public void OnJoinButtonClick()
    16.     {
    17.         NetworkManager.Singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes(passwordField.text);
    18.         NetworkManager.Singleton.StartClient();
    19.     }
    20.  
    21.     private void ApprovalCheck(byte[] connectionData, ulong userID, NetworkManager.ConnectionApprovedDelegate callback)
    22.     {
    23.         print("here");
    24.         if (NetworkManager.Singleton.IsHost)
    25.         {
    26.             print("Yep");
    27.         }
    28.         else
    29.         {
    30.             string password = Encoding.ASCII.GetString(connectionData);
    31.  
    32.             if (password == passwordField.text)
    33.             {
    34.                 print("Yep");
    35.             }
    36.             else
    37.             {
    38.                 print("Nope");
    39.             }
    40.         }
    41.     }
    42. }
    I always get different errors even if I do it in the same way. It's a very strange thing...
     
  32. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    You should be able to subscribe just before the StartHost/Client calls. Here's the test code I use, the EnableCallbacks are just doing multiple subscribes in one call.
    Code (CSharp):
    1. public class StartSceneController : MonoBehaviour
    2. {
    3.     ServerNetworkCallbackService serverNetworkCallbackService = new ServerNetworkCallbackService();
    4.     ClientNetworkCallbackService clientNetworkCallbackService = new ClientNetworkCallbackService();
    5.  
    6.     private void Start()
    7.     {
    8.         Application.targetFrameRate = 15;
    9.     }
    10.  
    11.     public void OnClickStartHost()
    12.     {
    13.         serverNetworkCallbackService.EnableCallbacks();
    14.         NetworkManager.Singleton.StartHost();
    15.         NetworkManager.Singleton.SceneManager.LoadScene("RpcScene", LoadSceneMode.Single);
    16.     }
    17.  
    18.     public void OnClickStartClient()
    19.     {
    20.         clientNetworkCallbackService.EnableCallbacks();
    21.         NetworkManager.Singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes("TEST");
    22.  
    23.         Debug.Log("connectionData: " +  NetworkManager.Singleton.NetworkConfig.ConnectionData.Length);
    24.         NetworkManager.Singleton.StartClient();
    25.     }
    26.  
    27.     private void OnDestroy()
    28.     {
    29.     //    serverNetworkCallbackService.DisableCallbacks();
    30.     }
    31. }
     
  33. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Something like that?
    For this variant callback still doesn't work
    Code (CSharp):
    1.     public void OnHostButtonClick()
    2.     {
    3.         NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck;
    4.         NetworkManager.Singleton.StartHost();
    5.     }
    6.    
    7.     public void OnJoinButtonClick()
    8.     {
    9.         NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck;
    10.         NetworkManager.Singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes(passwordField.text);
    11.         NetworkManager.Singleton.StartClient();
    12.     }
     
  34. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    664
    What errors are you seeing?

    You might want to remove everything that isn't essential for the test to narrow down the problem. Just a game object and MonoBehaviour script for the button handling and callback, and the NetworkManager.
     
  35. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    So I remade everything from zero. Now it works.

    Code (CSharp):
    1. public class RoomConnection : MonoBehaviour
    2. {
    3.     [SerializeField] private TMP_InputField passwordField;
    4.    
    5.     public void Host()
    6.     {
    7.         NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck;
    8.        
    9.         SavePassword(passwordField.text);
    10.  
    11.         NetworkManager.Singleton.StartHost();
    12.     }
    13.    
    14.     public void Join()
    15.     {
    16.         NetworkManager.Singleton.NetworkConfig.ConnectionData = Encoding.ASCII.GetBytes(passwordField.text);
    17.         NetworkManager.Singleton.StartClient();
    18.     }
    19.    
    20.     private void ApprovalCheck(byte[] connectionData, ulong clientID, NetworkManager.ConnectionApprovedDelegate callback)
    21.     {
    22.         bool approve = Encoding.ASCII.GetString(connectionData) == GetPassword();
    23.        
    24.         callback(true, null, approve, Vector3.zero, Quaternion.identity);
    25.     }
    26.  
    27.     private void SavePassword(string password)
    28.     {
    29.         PlayerPrefs.SetString("RoomPassword", password);
    30.     }
    31.  
    32.     private string GetPassword()
    33.     {
    34.         try
    35.         {
    36.             return PlayerPrefs.GetString("RoomPassword");
    37.         }
    38.         catch (Exception e)
    39.         {
    40.             Debug.Log(e);
    41.             return null;
    42.         }
    43.     }
    44. }
    Huge thanks to you guys!
     
    ProGameDevUser, CosmoM and cerestorm like this.