Search Unity

Question Load client to the same scene as the host

Discussion in 'Relay' started by internet_maze, Jan 6, 2023.

  1. internet_maze

    internet_maze

    Joined:
    Oct 29, 2022
    Posts:
    1
    I have a client that connects to the host using the code provided to the host when they create a relay.
    The connection works fine, but the client does not load the same scene as the host.
    I've already tried using the network scene manager

    Code (CSharp):
    1. public class NetworkSceneChange : NetworkBehaviour
    2. {
    3.     [SerializeField] private string sceneName;
    4.     [SerializeField] private Button sceneChangeBtn;
    5.  
    6.     public override void OnNetworkSpawn()
    7.     {
    8.    
    9.         sceneChangeBtn.onClick.AddListener(() =>
    10.         {
    11.             NetworkManager.SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
    12.         });
    13.        
    14.     }
    15.  
    16. }
    However, the host does not load into the scene for some reason. Only loads in when I use a normal scene manager.
    Basically, I need the host to load a scene and for the client to load on the same scene after connecting.
     
  2. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    @internet_maze
    I think I can help you with this.
    The first thing to check is your project's Build Settings-->Scenes in Build list.
    upload_2023-1-6_9-43-16.png
    Any scene you want to be able to synchronize between the host/server and clients when using the NetworkSceneManager needs to be in this list.

    There are two ways scenes will get synchronized with clients:
    1. Scenes already loaded by the server prior to starting NetworkManager
      1. Scenes loaded on the server side before the sever is started will be automatically synchronized with clients when they first connect. You can use the normal Unity SceneManager for this, but you need to make sure they are loaded prior to starting the server.
    2. Scenes loaded after the NetworkManager has been started.
      1. Once the server has started, you should always use the NetworkManager.SceneManager (NetworkSceneManager). You can subscribe to NetworkManager.OnServerStarted to know when it is safe for a server to load a scene via NetworkManager.SceneManager.
      2. Scenes loaded via the NetworkSceneManager (NetworkManager.SceneManager) will automatically get loaded on already connected clients and will be loaded by clients that connect after the scene(s) has/have been loaded (i.e. late joining clients).
    I would also highly recommend reading over the the NGO Integrated Scene Management sections starting with:
    Using NetworkSceneManager | Unity Multiplayer Networking (unity3d.com)

    Additionally, here are two places you can find scene loading examples:
    The NGO TestProject contains some scene loading examples that might be useful to look through as well.

    NGO Bootstrap Usage Pattern
    If you want to start with an "all in one" project that uses an NGO bootstrap usage pattern, you can look at a small "quick start" project of mine (link above) that has a detailed walk through of the components in the project and gets you up and running with an already existing framework that you can expand upon.

    If none of the above helps and you are still running into issues, then if you could you provide me with:
    • The version of Netcode for GameObjects you are using
    • The version of Unity you are using
    • More details of the sequence of events (or a repository link would be the fastest way)
    I will better be able to help you troubleshoot.
     
  3. zyleo

    zyleo

    Joined:
    Apr 14, 2023
    Posts:
    1
    Awesome answer, thanks a lot.
     
  4. rayanghosh99

    rayanghosh99

    Joined:
    Sep 4, 2020
    Posts:
    18
    @NoelStephens_Unity Thank you for the details.

    I am seeking answers, My case is bit weird,

    Whenever I join a host, using the following script, Clients are loading an additional scene.
    NO Scene load happen during joining... this bug is very sneaky...

    Code (CSharp):
    1.  public async void CreateOrJoinLobby()
    2. {
    3.      MP_pannel.SetActive(true);
    4.      _connectedLobby = await QuickJoinLobby() ?? await CreateLobby();
    5. }
    6.  
    7. private async Task<Lobby> QuickJoinLobby()
    8. {
    9.      try
    10.      {
    11.          // Attempt to join a lobby in progress
    12.          var lobby = await Lobbies.Instance.QuickJoinLobbyAsync();
    13.  
    14.          // If we found one, grab the relay allocation details
    15.          var a = await RelayService.Instance.JoinAllocationAsync(lobby.Data[JoinCodeKey].Value);
    16.  
    17.          // Set the details to the transform
    18.          SetTransformAsClient(a);
    19.  
    20.          // Join the game room as a client
    21.          NetworkManager.Singleton.StartClient();
    22.          return lobby;
    23.      }
    24.      catch (Exception e)
    25.      {
    26.          Debug.Log($"No lobbies available via quick join, " + e);
    27.          return null;
    28.      }
    29. }
    30.  
    31. private async Task<Lobby> CreateLobby()
    32. {
    33.    
    34.      try
    35.      {
    36.          const int maxPlayers = 2;
    37.  
    38.          // Create a relay allocation and generate a join code to share with the lobby
    39.          var a = await RelayService.Instance.CreateAllocationAsync(maxPlayers);
    40.          var joinCode = await RelayService.Instance.GetJoinCodeAsync(a.AllocationId);
    41.  
    42.          // Create a lobby, adding the relay join code to the lobby data
    43.          var options = new CreateLobbyOptions
    44.          {
    45.              Data = new Dictionary<string, DataObject> { { JoinCodeKey, new DataObject(DataObject.VisibilityOptions.Public, joinCode) } }
    46.          };
    47.          var lobby = await Lobbies.Instance.CreateLobbyAsync("Useless Lobby Name", maxPlayers, options);
    48.  
    49.          // Send a heartbeat every 15 seconds to keep the room alive
    50.          StartCoroutine(HeartbeatLobbyCoroutine(lobby.Id, 15));
    51.  
    52.          // Set the game room to use the relay allocation
    53.          _transport.SetHostRelayData(a.RelayServer.IpV4, (ushort)a.RelayServer.Port, a.AllocationIdBytes, a.Key, a.ConnectionData);
    54.  
    55.          // Start the room. I'm doing this immediately, but maybe you want to wait for the lobby to fill up
    56.          NetworkManager.Singleton.StartHost();
    57.          return lobby;
    58.      }
    59.      catch (Exception e)
    60.      {
    61.          Debug.LogFormat("Failed creating a lobby, " + e);
    62.          return null;
    63.      }
    64. }
     
  5. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    You might need use Additive Client Synchronization mode that can be set via the NetworkManager.SceneManager.SetClientSynchronizationMode method on the server or host after it has been started like in the below pseudo code example:
    Code (CSharp):
    1. using Unity.Netcode;
    2. using UnityEngine;
    3. using UnityEngine.SceneManagement;
    4.  
    5. public class MyHostStartupClass : MonoBehaviour
    6. {
    7.     public NetworkManager NetworkManager;
    8.     // Invoked by button or the like
    9.     public void OnStartHost()
    10.     {
    11.         NetworkManager.OnServerStarted += OnServerStarted;
    12.         NetworkManager.StartHost();
    13.     }
    14.  
    15.     private void OnServerStarted()
    16.     {
    17.         NetworkManager.OnServerStarted -= OnServerStarted;
    18.         // This only needs to be set on the host or server side and is sent to clients during synchronization.
    19.         // This setting tells clients to re-use scenes that are already loaded.
    20.         NetworkManager.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive);
    21.     }
    22. }
    Alternately, if this isn't clients loading the same scene multiple times kind of an issue you might look over scene validation.
     
    aardvark2012 and rayanghosh99 like this.
  6. rayanghosh99

    rayanghosh99

    Joined:
    Sep 4, 2020
    Posts:
    18
    Many thanks! These (to the exact point) insights are highly appreciated;
    The links you have shared are super useful.

    Thank you. Thank god I came across the right thread.
     
  7. UnityMersus

    UnityMersus

    Joined:
    Jun 27, 2017
    Posts:
    3
    I have project using Oculus SDK, Netcode for gameobjects. So I have Network manager with prefab list. Also I have script for spawning objects. But the problem is the client can not interact with spawned like Host (can grab objects and interact).

    public class SpawnerControl : NetworkBehaviour
    {
    public NetworkManager manager;
    [SerializeField] private GameObject _torchObjectPrefab;
    [SerializeField] private GameObject _cubePrefab;
    private bool hasSpawned = false;
    private ulong clientId;
    public override void OnNetworkSpawn()
    {
    SpawnObjectsServerRpc(); // serverRpcParams will be filled in automatically
    }
    //[SerializeField] private int maxObjectInstanceCount = 2; // how many objects would like to have
    private void OnEnable()
    {
    //manager.OnServerStarted += SpawnObjects;
    //manager.OnServerStarted += SpawnObjectsServerRpc;
    //manager.OnServerStarted += SpawnObjectsServerRpc;
    }
    private void OnDisable()
    {
    //manager.OnServerStarted -= SpawnObjects;
    //manager.OnServerStarted -= SpawnObjectsServerRpc;
    //manager.OnServerStarted -= SpawnObjectsServerRpc;
    }
    #region ServerRpc method not working
    //[ServerRpc(RequireOwnership = false)]
    //public void SpawnObjectsServerRpc(ServerRpcParams serverRpcParams = default)
    //{
    // // Get the client ID of the sender
    // ulong clientId = serverRpcParams.Receive.SenderClientId;
    // // Check if the sender is a connected client
    // if (NetworkManager.Singleton.ConnectedClients.TryGetValue(clientId, out var networkedClient))
    // {
    // // Get the client instance
    // var client = NetworkManager.Singleton.ConnectedClients[clientId];
    // // Do things for this client if needed
    // }
    // // Spawn the torch object
    // var torchInstance = Instantiate(_torchObjectPrefab);
    // var torchNetworkObject = torchInstance.GetComponent<NetworkObject>();
    // torchNetworkObject.Spawn(); // Mark the object for spawning across the network
    // // Change ownership of the torch object to the client who requested the spawn
    // torchNetworkObject.ChangeOwnership(clientId);
    // // Spawn the cube object
    // var cubeInstance = Instantiate(_cubePrefab);
    // var cubeNetworkObject = cubeInstance.GetComponent<NetworkObject>();
    // cubeNetworkObject.Spawn(); // Mark the object for spawning across the network
    // // Change ownership of the cube object to the client who requested the spawn
    // cubeNetworkObject.ChangeOwnership(clientId);
    // hasSpawned = true;
    //}
    #endregion
    #region ServerRpc test1
    //[ServerRpc]
    ////[ServerRpc(RequireOwnership = false)]
    //public void SpawnObjectsServerRpc()
    //{
    // #region Spawn
    // //var instance = Instantiate(_torchObjectPrefab);
    // //var instance1 = Instantiate(_cube);
    // //var instanceNetworkObject = instance.GetComponent<NetworkObject>();
    // //var instanceNetworkObject1 = instance1.GetComponent<NetworkObject>();
    // //instanceNetworkObject.Spawn();
    // //instanceNetworkObject1.Spawn();
    // //instanceNetworkObject.RemoveOwnership();
    // //instanceNetworkObject1.RemoveOwnership();
    // #endregion
    // // Spawn the object on the server
    // var instance = Instantiate(_torchObjectPrefab);
    // var instanceNetworkObject = instance.GetComponent<NetworkObject>();
    // instanceNetworkObject.Spawn();
    // //instanceNetworkObject.SpawnWithOwnership(clientId);
    // //GetComponent <NetworkObject>().ChangeOwnership(OwnerClientId);
    // Debug.Log("Owner client id is: " + OwnerClientId);
    // hasSpawned = true;
    //}
    #endregion
    #region ServerRpc test2
    [ServerRpc]
    private void SpawnObjectsServerRpc()
    {
    // Spawn the object on the server
    //GameObject instance = Instantiate(_torchObjectPrefab);
    #region Network spawn
    //if (instance != null)
    //{
    // NetworkObject instanceNetworkObject = instance.GetComponent<NetworkObject>();
    // if (instanceNetworkObject != null)
    // {
    // // Ensure clientId is set to the correct client ID you want to grant ownership to
    // // Replace clientId with the appropriate client ID variable
    // int clientId = GetClientIdToGrantOwnership();
    // instanceNetworkObject.SpawnWithOwnership(clientId);
    // }
    // else
    // {
    // Debug.LogError("NetworkObject component not found on instantiated object.");
    // }
    //}
    //else
    //{
    // Debug.LogError("Failed to instantiate _torchObjectPrefab.");
    //}
    #endregion
    #region Spawn
    //var instance = Instantiate(_torchObjectPrefab);
    //var instance1 = Instantiate(_cube);
    //var instanceNetworkObject = instance.GetComponent<NetworkObject>();
    //var instanceNetworkObject1 = instance1.GetComponent<NetworkObject>();
    //instanceNetworkObject.Spawn();
    //instanceNetworkObject1.Spawn();
    //instanceNetworkObject.RemoveOwnership();
    //instanceNetworkObject1.RemoveOwnership();
    #endregion
    #region Spawn2
    //GetComponent<NetworkObject>().ChangeOwnership(NetworkManager.Singleton.ServerClientId);
    //Spawn the object on the server
    var instance = Instantiate(_torchObjectPrefab);
    var instanceNetworkObject = instance.GetComponent<NetworkObject>();
    instanceNetworkObject.Spawn();
    instanceNetworkObject.SpawnWithOwnership(clientId);
    Debug.Log("Owner client id is: " + clientId);
    //GetComponent <NetworkObject>().ChangeOwnership(OwnerClientId);
    //Debug.Log("Owner client id is: " + OwnerClientId);
    hasSpawned = true;
    #endregion
    }
     
  8. UnityMersus

    UnityMersus

    Joined:
    Jun 27, 2017
    Posts:
    3
    Sorry about so many comments
     
  9. MiTschMR

    MiTschMR

    Joined:
    Aug 28, 2018
    Posts:
    487
    Please use code blocks instead of plain text:
    upload_2024-3-25_12-15-48.png

    Makes it much easier to read. See the "Code" section and highlighted button.
     
  10. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    Make sure you are using an owner authoritative NetworkTransform (also known as a "ClientNetworkTransform") and not just the default NetworkTransform if you want clients to be able to move owned objects around.
    Here is an example of an owner authoritative NetworkTransform:
    Code (CSharp):
    1. public class OwnerAuthoritativeNetworkTransform : NetworkTransform
    2. {
    3.     // This is all that is needed to make a NetworkTransform owner authoritative
    4.     protected override bool OnIsServerAuthoritative()
    5.     {
    6.         return false;
    7.     }
    8. }
    Let me know if this resolves your issue?