MultiSceneNetManager.cs 5.7 KB

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