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
public class NetworkSceneChange : NetworkBehaviour
{
[SerializeField] private string sceneName;
[SerializeField] private Button sceneChangeBtn;
public override void OnNetworkSpawn()
{
sceneChangeBtn.onClick.AddListener(() =>
{
NetworkManager.SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
});
}
}
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.
@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.
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:
Scenes already loaded by the server prior to starting NetworkManager
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.
Scenes loaded after the NetworkManager has been started.
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.
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).
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)
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…
public async void CreateOrJoinLobby()
{
MP_pannel.SetActive(true);
_connectedLobby = await QuickJoinLobby() ?? await CreateLobby();
}
private async Task<Lobby> QuickJoinLobby()
{
try
{
// Attempt to join a lobby in progress
var lobby = await Lobbies.Instance.QuickJoinLobbyAsync();
// If we found one, grab the relay allocation details
var a = await RelayService.Instance.JoinAllocationAsync(lobby.Data[JoinCodeKey].Value);
// Set the details to the transform
SetTransformAsClient(a);
// Join the game room as a client
NetworkManager.Singleton.StartClient();
return lobby;
}
catch (Exception e)
{
Debug.Log($"No lobbies available via quick join, " + e);
return null;
}
}
private async Task<Lobby> CreateLobby()
{
try
{
const int maxPlayers = 2;
// Create a relay allocation and generate a join code to share with the lobby
var a = await RelayService.Instance.CreateAllocationAsync(maxPlayers);
var joinCode = await RelayService.Instance.GetJoinCodeAsync(a.AllocationId);
// Create a lobby, adding the relay join code to the lobby data
var options = new CreateLobbyOptions
{
Data = new Dictionary<string, DataObject> { { JoinCodeKey, new DataObject(DataObject.VisibilityOptions.Public, joinCode) } }
};
var lobby = await Lobbies.Instance.CreateLobbyAsync("Useless Lobby Name", maxPlayers, options);
// Send a heartbeat every 15 seconds to keep the room alive
StartCoroutine(HeartbeatLobbyCoroutine(lobby.Id, 15));
// Set the game room to use the relay allocation
_transport.SetHostRelayData(a.RelayServer.IpV4, (ushort)a.RelayServer.Port, a.AllocationIdBytes, a.Key, a.ConnectionData);
// Start the room. I'm doing this immediately, but maybe you want to wait for the lobby to fill up
NetworkManager.Singleton.StartHost();
return lobby;
}
catch (Exception e)
{
Debug.LogFormat("Failed creating a lobby, " + e);
return null;
}
}
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:
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MyHostStartupClass : MonoBehaviour
{
public NetworkManager NetworkManager;
// Invoked by button or the like
public void OnStartHost()
{
NetworkManager.OnServerStarted += OnServerStarted;
NetworkManager.StartHost();
}
private void OnServerStarted()
{
NetworkManager.OnServerStarted -= OnServerStarted;
// This only needs to be set on the host or server side and is sent to clients during synchronization.
// This setting tells clients to re-use scenes that are already loaded.
NetworkManager.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive);
}
}
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();
// 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();
// 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();
// //var instanceNetworkObject1 = instance1.GetComponent();
// //instanceNetworkObject.Spawn();
// //instanceNetworkObject1.Spawn();
// //instanceNetworkObject.RemoveOwnership();
// //instanceNetworkObject1.RemoveOwnership();
// #endregion
// // Spawn the object on the server
// var instance = Instantiate(_torchObjectPrefab);
// var instanceNetworkObject = instance.GetComponent();
// instanceNetworkObject.Spawn();
// //instanceNetworkObject.SpawnWithOwnership(clientId);
// //GetComponent ().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();
// 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();
//var instanceNetworkObject1 = instance1.GetComponent(); //instanceNetworkObject.Spawn(); //instanceNetworkObject1.Spawn(); //instanceNetworkObject.RemoveOwnership(); //instanceNetworkObject1.RemoveOwnership(); #endregion #region Spawn2
//GetComponent().ChangeOwnership(NetworkManager.Singleton.ServerClientId);
//Spawn the object on the server
var instance = Instantiate(_torchObjectPrefab);
var instanceNetworkObject = instance.GetComponent();
instanceNetworkObject.Spawn();
instanceNetworkObject.SpawnWithOwnership(clientId);
Debug.Log("Owner client id is: " + clientId);
//GetComponent ().ChangeOwnership(OwnerClientId); //Debug.Log("Owner client id is: " + OwnerClientId);
hasSpawned = true; #endregion
}
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:
public class OwnerAuthoritativeNetworkTransform : NetworkTransform
{
// This is all that is needed to make a NetworkTransform owner authoritative
protected override bool OnIsServerAuthoritative()
{
return false;
}
}