MultiSceneNetManager.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.SceneManagement;
  5. namespace Mirror.Examples.MultipleAdditiveScenes
  6. {
  7. [AddComponentMenu("")]
  8. public class MultiSceneNetManager : NetworkManager
  9. {
  10. [Header("Spawner Setup")]
  11. [Tooltip("Reward Prefab for the Spawner")]
  12. public GameObject rewardPrefab;
  13. [Header("MultiScene Setup")]
  14. public int instances = 3;
  15. [Scene]
  16. public string gameScene;
  17. // This is set true after server loads all subscene instances
  18. bool subscenesLoaded;
  19. // subscenes are added to this list as they're loaded
  20. readonly List<Scene> subScenes = new List<Scene>();
  21. // Sequential index used in round-robin deployment of players into instances and score positioning
  22. int clientIndex;
  23. public static new MultiSceneNetManager singleton { get; private set; }
  24. /// <summary>
  25. /// Runs on both Server and Client
  26. /// Networking is NOT initialized when this fires
  27. /// </summary>
  28. public override void Awake()
  29. {
  30. base.Awake();
  31. singleton = this;
  32. }
  33. #region Server System Callbacks
  34. /// <summary>
  35. /// Called on the server when a client adds a new player with NetworkClient.AddPlayer.
  36. /// <para>The default implementation for this function creates a new player object from the playerPrefab.</para>
  37. /// </summary>
  38. /// <param name="conn">Connection from client.</param>
  39. public override void OnServerAddPlayer(NetworkConnectionToClient conn)
  40. {
  41. StartCoroutine(OnServerAddPlayerDelayed(conn));
  42. }
  43. // This delay is mostly for the host player that loads too fast for the
  44. // server to have subscenes async loaded from OnStartServer ahead of it.
  45. IEnumerator OnServerAddPlayerDelayed(NetworkConnectionToClient conn)
  46. {
  47. // wait for server to async load all subscenes for game instances
  48. while (!subscenesLoaded)
  49. yield return null;
  50. // Send Scene message to client to additively load the game scene
  51. conn.Send(new SceneMessage { sceneName = gameScene, sceneOperation = SceneOperation.LoadAdditive });
  52. // Wait for end of frame before adding the player to ensure Scene Message goes first
  53. yield return new WaitForEndOfFrame();
  54. base.OnServerAddPlayer(conn);
  55. PlayerScore playerScore = conn.identity.GetComponent<PlayerScore>();
  56. playerScore.playerNumber = clientIndex;
  57. playerScore.scoreIndex = clientIndex / subScenes.Count;
  58. playerScore.matchIndex = clientIndex % subScenes.Count;
  59. // Do this only on server, not on clients
  60. // This is what allows Scene Interest Management
  61. // to isolate matches per scene instance on server.
  62. if (subScenes.Count > 0)
  63. SceneManager.MoveGameObjectToScene(conn.identity.gameObject, subScenes[clientIndex % subScenes.Count]);
  64. clientIndex++;
  65. }
  66. #endregion
  67. #region Start & Stop Callbacks
  68. /// <summary>
  69. /// This is invoked when a server is started - including when a host is started.
  70. /// <para>StartServer has multiple signatures, but they all cause this hook to be called.</para>
  71. /// </summary>
  72. public override void OnStartServer()
  73. {
  74. StartCoroutine(ServerLoadSubScenes());
  75. }
  76. // We're additively loading scenes, so GetSceneAt(0) will return the main "container" scene,
  77. // therefore we start the index at one and loop through instances value inclusively.
  78. // If instances is zero, the loop is bypassed entirely.
  79. IEnumerator ServerLoadSubScenes()
  80. {
  81. for (int index = 1; index <= instances; index++)
  82. {
  83. yield return SceneManager.LoadSceneAsync(gameScene, new LoadSceneParameters { loadSceneMode = LoadSceneMode.Additive, localPhysicsMode = LocalPhysicsMode.Physics3D });
  84. Scene newScene = SceneManager.GetSceneAt(index);
  85. subScenes.Add(newScene);
  86. Spawner.InitialSpawn(newScene);
  87. }
  88. subscenesLoaded = true;
  89. }
  90. /// <summary>
  91. /// This is called when a server is stopped - including when a host is stopped.
  92. /// </summary>
  93. public override void OnStopServer()
  94. {
  95. NetworkServer.SendToAll(new SceneMessage { sceneName = gameScene, sceneOperation = SceneOperation.UnloadAdditive });
  96. StartCoroutine(ServerUnloadSubScenes());
  97. clientIndex = 0;
  98. }
  99. // Unload the subScenes and unused assets and clear the subScenes list.
  100. IEnumerator ServerUnloadSubScenes()
  101. {
  102. for (int index = 0; index < subScenes.Count; index++)
  103. if (subScenes[index].IsValid())
  104. yield return SceneManager.UnloadSceneAsync(subScenes[index]);
  105. subScenes.Clear();
  106. subscenesLoaded = false;
  107. yield return Resources.UnloadUnusedAssets();
  108. }
  109. /// <summary>
  110. /// This is called when a client is stopped.
  111. /// </summary>
  112. public override void OnStopClient()
  113. {
  114. // Make sure we're not in ServerOnly mode now after stopping host client
  115. if (mode == NetworkManagerMode.Offline)
  116. StartCoroutine(ClientUnloadSubScenes());
  117. }
  118. // Unload all but the active scene, which is the "container" scene
  119. IEnumerator ClientUnloadSubScenes()
  120. {
  121. for (int index = 0; index < SceneManager.sceneCount; index++)
  122. if (SceneManager.GetSceneAt(index) != SceneManager.GetActiveScene())
  123. yield return SceneManager.UnloadSceneAsync(SceneManager.GetSceneAt(index));
  124. }
  125. #endregion
  126. }
  127. }