the sample project. GitHub - Unity-Technologies/com.unity.services.samples.matchplay: A Matchmaking with Multiplay Unity example project. Implements the Unity Matchmaking and Multiplay SDK to create an end to end matchmaking game experience.
The project is this one. I have made some changes but most of my changes were outside of the part where I think it is bugging.
I think I find the solution my client-server connection is stable now.
The problem is on the class MatchplayNetworkServer and in the Class ServerGameManager on the
On the server game Manager we have this method:
public async Task StartGameServerAsync(GameInfo startingGameInfo)
{
Debug.Log($"Starting server with:{startingGameInfo}.");
// The server should respond to query requests irrespective of the server being allocated.
// Hence, start the handler as soon as we can.
await m_MultiplayServerQueryService.BeginServerQueryHandler();
try
{
var matchmakerPayload = await GetMatchmakerPayload(k_MultiplayServiceTimeout);
if (matchmakerPayload != null)
{
Debug.Log($"Got payload: {matchmakerPayload}");
startingGameInfo = PickGameInfo(matchmakerPayload);
MatchStartedServerQuery(startingGameInfo);
await StartBackfill(matchmakerPayload, startingGameInfo);
m_NetworkServer.OnPlayerJoined += UserJoinedServer;
m_NetworkServer.OnPlayerLeft += UserLeft;
m_StartedServices = true;
}
else
{
Debug.LogWarning("Getting the Matchmaker Payload timed out, starting with defaults.");
}
}
catch (Exception ex)
{
Debug.LogWarning($"Something went wrong trying to set up the Services:\n{ex} ");
}
if (!m_NetworkServer.OpenConnection(m_ServerIP, m_ServerPort, startingGameInfo))
{
Debug.LogError("NetworkServer did not start as expected.");
return;
}
//Changes Map and sets the synched shared variables to the starting info
m_SynchedServerData = await m_NetworkServer.ConfigureServer(startingGameInfo);
if (m_SynchedServerData == null)
{
Debug.LogError("Could not find the synchedServerData.");
return;
}
m_SynchedServerData.serverID.Value = m_ServerName;
m_SynchedServerData.map.OnValueChanged += OnServerChangedMap;
m_SynchedServerData.gameMode.OnValueChanged += OnServerChangedMode;
//Set the count to connected players
m_MultiplayServerQueryService.SetPlayerCount((ushort)NetworkServer.PlayerCount);
}
This code is responsible for starting the server Multiplay Server Query Service connecting the clients, Open Connection and start the server. After all that he call the method NetworkServer.ConfigureServer with calls the
m_NetworkManager.SceneManager.LoadScene(startingGameInfo.ToSceneName, LoadSceneMode.Single);
The NetworkManager sceneManager load scene is completely unreliable. My client was connecting to the server before he completely load the correct scene and unload the previews scene with the bootstrap scene.
-
To Correct that the first thing I have done is a Scene manager class to receive and handle all the load and unload calls. I called NetworkSceneManager.
-
on my StartGameServerAsync the first thing that I do is to call the NetworkServer.ConfigureServer before everything and I make the server wait for My battle scene to be fully loaded and my bootstrap scene to be fully unloaded even before I open the server connection or start the BeginServerQueryHandler.
-
Another thing is I am not using the NetworkManager.SceneManager.LoadScene(). i chose to use the normal SceneManager.LoadScene because I load the scene before starting the server and only start after the process is fully completed on the server.
I am adding bellow my code for reference from this methods
//Changes Map and sets the synched shared variables to the starting info
bool serverSetup = await networkServer.ConfigureServer(startingGameInfo);
if (serverSetup == false)
{
Debug.LogError("Could not setup the server");
return;
}
if (!networkServer.OpenConnection(serverIP, serverPort, startingGameInfo))
{
Debug.LogError("NetworkServer did not start as expected.");
return;
}
NetworkSceneManager.Instance.InitNetworkSceneManager();
Debug.Log($"Starting server with:{startingGameInfo}.");
// The server should respond to query requests irrespective of the server being allocated.
// Hence, start the handler as soon as we can.
await multiplayServerQueryService.BeginServerQueryHandler();
try
{
var matchmakerPayload = await GetMatchmakerPayload(multiplayServiceTimeout);
if (matchmakerPayload != null)
{
Debug.Log($"Got payload: {matchmakerPayload}");
startingGameInfo = PickGameInfo(matchmakerPayload);
MatchStartedServerQuery(startingGameInfo);
await StartBackfill(matchmakerPayload, startingGameInfo);
networkServer.OnPlayerJoined += UserJoinedServer;
networkServer.OnPlayerLeft += UserLeft;
startedServices = true;
}
else
{
Debug.LogWarning("Getting the Matchmaker Payload timed out, starting with defaults.");
}
}
catch (Exception ex)
{
Debug.LogWarning($"Something went wrong trying to set up the Services:\n{ex} ");
}
var localNetworkedSceneLoaded = false;
//NetworkSceneManager.Instance.LoadNetworkScene(startingGameInfo.ToSceneName, LoadSceneMode.Additive, (ulong clientId, string sceneName, LoadSceneMode sceneMode) =>
//{
// void bootStrapUnloadCompleted()
// {
// localNetworkedSceneLoaded = true;
// NetworkSceneManager.OnBattleSceneLoaded -= bootStrapUnloadCompleted;
// }
// NetworkSceneManager.OnBattleSceneLoaded += bootStrapUnloadCompleted;
//});
NetworkSceneManager.Instance.IsServer = true;
NetworkSceneManager.Instance.LoadScene(startingGameInfo.ToSceneName, LoadSceneMode.Additive, (Scene sceneName, LoadSceneMode sceneMode) =>
{
void bootStrapUnloadCompleted()
{
localNetworkedSceneLoaded = true;
NetworkSceneManager.OnBattleSceneLoaded -= bootStrapUnloadCompleted;
}
NetworkSceneManager.OnBattleSceneLoaded += bootStrapUnloadCompleted;
});
while (!localNetworkedSceneLoaded) await Task.Delay(50);
await Task.Delay(1000);
Debug.Log("Battle scene loading completed");
return true;
public class NetworkSceneManager : MonoBehaviour
{
private bool isServer = false;
public bool IsServer
{
get { return isServer; }
set { isServer = value; }
}
public static NetworkSceneManager Instance
{
get
{
if (networkSceneManager != null) return networkSceneManager;
networkSceneManager = FindObjectOfType<NetworkSceneManager>();
if (networkSceneManager == null)
{
Debug.LogError("No NetworkSceneManager in scene, did you run this from the bootStrap scene?");
return null;
}
return networkSceneManager;
}
}
private Scene previewActiveScene;
public static Action OnBattleSceneLoaded;
private static NetworkSceneManager networkSceneManager;
private NetworkManager networkManager;
#region properties
public void Awake()
{
networkManager = GetComponent<NetworkManager>();
SceneManager.sceneLoaded += OnSceneLoaded;
SceneManager.sceneUnloaded += OnSceneUnloaded;
}
public void InitNetworkSceneManager()
{
networkManager.SceneManager.OnLoad += OnSceneNetworkLoaded;
networkManager.SceneManager.OnUnload += OnSceneNetworkUnloaded;
}
public void DisposeNetworkSceneManager()
{
networkManager.SceneManager.OnLoad -= OnSceneNetworkLoaded;
networkManager.SceneManager.OnUnload -= OnSceneNetworkUnloaded;
}
#endregion
#region privateMethods
private void OnSceneLoaded(Scene scene,LoadSceneMode loadMode)
{
Debug.Log("Loaded Scene: " + scene.name);
if(IsServer && scene.name == "BattleArena")
{
SceneManager.SetActiveScene(scene);
UnloadSceneAsync(0);
} else if(networkManager.IsClient)
{
}
}
private void OnSceneUnloaded(Scene scene)
{
Debug.Log("UnLoaded Scene: " + scene.name);
if (IsServer && scene.name == "BootStrap")
{
OnBattleSceneLoaded?.Invoke();
}
}
private void OnSceneNetworkLoaded(ulong clientId, string sceneName, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation)
{
Debug.Log("Loaded Network Scene: " + sceneName);
}
private void OnSceneNetworkUnloaded(ulong clientId, string sceneName, AsyncOperation asyncOperation)
{
Debug.Log("Unload Network Scene: " + sceneName);
}
#endregion
#region publicMethods
public void LoadNetworkScene(string sceneName, LoadSceneMode mode = LoadSceneMode.Single, Action<ulong, string, LoadSceneMode> OnCompleteLoading = null)
{
Debug.Log("Loading Network Scene: " + sceneName);
networkManager.SceneManager.LoadScene(sceneName, mode);
if (OnCompleteLoading != null)
{
networkManager.SceneManager.OnLoadComplete += CreateAndSetSynchedServerData;
void CreateAndSetSynchedServerData(ulong clientId, string sceneName, LoadSceneMode sceneMode)
{
if (clientId != networkManager.LocalClientId)
return;
OnCompleteLoading.Invoke(clientId, sceneName, sceneMode);
networkManager.SceneManager.OnLoadComplete -= CreateAndSetSynchedServerData;
}
}
}
public void LoadScene(string sceneName, LoadSceneMode mode = LoadSceneMode.Single, Action<Scene, LoadSceneMode> OnCompleteLoading = null)
{
Debug.Log("Loading Scene: " + sceneName);
SceneManager.LoadScene(sceneName, mode);
if(OnCompleteLoading != null) {
SceneManager.sceneLoaded += CreateAndSetSynchedServerData;
void CreateAndSetSynchedServerData(Scene scene, LoadSceneMode sceneMode)
{
OnCompleteLoading.Invoke(scene, sceneMode);
SceneManager.sceneLoaded -= CreateAndSetSynchedServerData;
}
}
}
public void UnloadSceneAsync(Scene scene)
{
SceneManager.UnloadSceneAsync(scene);
}
public void UnloadSceneAsync(string sceneName)
{
SceneManager.UnloadSceneAsync(sceneName);
}
public void UnloadSceneAsync(int sceneIndex)
{
Debug.Log("unload scene: " + sceneIndex);
AsyncOperation loadOpt = SceneManager.UnloadSceneAsync(sceneIndex);
}
public Scene GetActiveScene() => SceneManager.GetActiveScene();
#endregion
}