Running into an unintended behavior where when the server loads into the game the connected clients make duplications to the objects held in DDOL.
I’m using a LoadSceneMode.Single transition, and the flow is Lobby > CharSelect > Loading > PersistentElements > GameScene.
I have a MultiplayerManager and the SceneLoadManager as singletons that are created at the Lobby stage, and running DDOL at Start(). The host creates a match and loads into the CharSelect scene, and other clients can then connect into the game where they also load in the CharSelect scene with no issues. However, once the game starts and the server loads in the PersistentElements scene I get a log error (via Awake() on the managers) that another instance exists.
I assume this has something to do with the Clients re-syncing, but I would think that would involve a complete reload vs duplication. It should also be noted that this only occurs in builds, not the editor, and the only reason i noticed was there were some UI elements stacking up.
Below is what’s called when everyone is ready and the start of the game;
if (allClientsReady)
{
GameLobby.Instance.DeleteLobby();
Loader.LoadNetwork(Loader.Scene.PersistentElementsScene, UnityEngine.SceneManagement.LoadSceneMode.Single);
}
Which calls the loading scene and sets a call back;
When a client returns to the lobby, are these “Managers” (*) going to re-instantiate themselves and put themselves a second time into DDOL? Because this is how DDOL duplication occurs. Use the debugger, set a breakpoint or at least log all Awake methods of objects that add themselves to DDOL.
The solution is simple: make the first scene the game loads contain all DDOL objects, and then transition to the next scene and never come back to the launch scene.
(*) = whatever “Manager” may imply - I’m never shying away from pointing out that “Manager” is a tradition more than anything. Here, one of your classes would perfectly make sense without the Manager as “SceneLoad(er)” whereas the other would simply be called “Multiplayer” (similar to the ubiquitous but extremely vague “GameManager”). What exactly does this “manage”? It may have a much more suitable name if you spend a few seconds rather than going for traditional names, perhaps it’s actually the OnlineGameSessionState or a NetworkConnectionHandler. This also discourages you from stuffing this class with all sorts of seemingly related functionality.
Called by everyone? In that case, that’ll be your problem. Only the server (host) loads the network session, none other.
Although this should occur in the editor too, unless you meant to say you’re only ever starting the host in the editor. Are you using Multiplayer Playmode?
Also note that non-network scene loads on the server (host) will be assumed to be networked scene loads by default. So if your host tries to locally load its HUD scene (additively) this host HUD will also be loaded for clients. Check the scene loading part of the manual for explanations and alternatives.
So the managers get created and set up on the Lobby scene, and when you’re in CharSelect or the “in-game” scenes you can only return to the MainMenu scene which has a script that destroys any persisting instances. There should only be one way to enter the Lobby scene, which is through the MainMenu. Both Main Menu scripts below;
public class MainMenuCleanup : MonoBehaviour
{
private void Awake()
{
if (NetworkManager.Singleton != null)
{
Destroy(NetworkManager.Singleton.gameObject);
}
if (MultiplayerManager.Instance != null)
{
Destroy(MultiplayerManager.Instance.gameObject);
}
if (GameLobby.Instance != null)
{
Destroy(GameLobby.Instance.gameObject);
}
if (SceneLoadManager.Instance != null)
{
Destroy(SceneLoadManager.Instance.gameObject);
}
}
}
For context on the scene flow and set up I followed the CodeMonkey multiplayer tutorial to set up the Lobby, CharSelect, and Relay. The check for if everyone is ready is done within a Server RPC; excerpt below.
I am not using Multiplayer Playmode (does that work for v2022?), but I have tried different combinations of Host/Server to Client via Build and Editor, and found that the error DOES in fact occur within the editor (as a client) if I start playing from the MainMenu scene (prior to this I was just loading straight into the Lobby scene).
Realizing this it seems your fix for starting on a game scene that includes all DDOL objects and then going into the main menu might be the right path, but I still don’t understand what is happening and why. The main menu should be far from relevant by the time we’re in the CharSelect scene, so why would starting there cause duplication of manager scripts (including the NetworkManager) that are spawned on the Lobby scene?
You are destroying the NetworkManager object. This is not supported and is known to cause issues.
And generally speaking, destroying any singleton object or component is just going to cause troubles. Singletons should be designed to be instantiated up front and never be destroyed.