MultiSceneNetManager.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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. #region Server System Callbacks
  24. /// <summary>
  25. /// Called on the server when a client adds a new player with NetworkClient.AddPlayer.
  26. /// <para>The default implementation for this function creates a new player object from the playerPrefab.</para>
  27. /// </summary>
  28. /// <param name="conn">Connection from client.</param>
  29. public override void OnServerAddPlayer(NetworkConnection conn)
  30. {
  31. StartCoroutine(OnServerAddPlayerDelayed(conn));
  32. }
  33. // This delay is mostly for the host player that loads too fast for the
  34. // server to have subscenes async loaded from OnStartServer ahead of it.
  35. IEnumerator OnServerAddPlayerDelayed(NetworkConnection conn)
  36. {
  37. // wait for server to async load all subscenes for game instances
  38. while (!subscenesLoaded)
  39. yield return null;
  40. // Send Scene message to client to additively load the game scene
  41. conn.Send(new SceneMessage { sceneName = gameScene, sceneOperation = SceneOperation.LoadAdditive });
  42. // Wait for end of frame before adding the player to ensure Scene Message goes first
  43. yield return new WaitForEndOfFrame();
  44. base.OnServerAddPlayer(conn);
  45. PlayerScore playerScore = conn.identity.GetComponent<PlayerScore>();
  46. playerScore.playerNumber = clientIndex;
  47. playerScore.scoreIndex = clientIndex / subScenes.Count;
  48. playerScore.matchIndex = clientIndex % subScenes.Count;
  49. clientIndex++;
  50. // Do this only on server, not on clients
  51. // This is what allows the NetworkSceneChecker on player and scene objects
  52. // to isolate matches per scene instance on server.
  53. if (subScenes.Count > 0)
  54. SceneManager.MoveGameObjectToScene(conn.identity.gameObject, subScenes[clientIndex % subScenes.Count]);
  55. }
  56. #endregion
  57. #region Start & Stop Callbacks
  58. /// <summary>
  59. /// This is invoked when a server is started - including when a host is started.
  60. /// <para>StartServer has multiple signatures, but they all cause this hook to be called.</para>
  61. /// </summary>
  62. public override void OnStartServer()
  63. {
  64. StartCoroutine(ServerLoadSubScenes());
  65. }
  66. // We're additively loading scenes, so GetSceneAt(0) will return the main "container" scene,
  67. // therefore we start the index at one and loop through instances value inclusively.
  68. // If instances is zero, the loop is bypassed entirely.
  69. IEnumerator ServerLoadSubScenes()
  70. {
  71. for (int index = 1; index <= instances; index++)
  72. {
  73. yield return SceneManager.LoadSceneAsync(gameScene, new LoadSceneParameters { loadSceneMode = LoadSceneMode.Additive, localPhysicsMode = LocalPhysicsMode.Physics3D });
  74. Scene newScene = SceneManager.GetSceneAt(index);
  75. subScenes.Add(newScene);
  76. Spawner.InitialSpawn(newScene);
  77. }
  78. subscenesLoaded = true;
  79. }
  80. /// <summary>
  81. /// This is called when a server is stopped - including when a host is stopped.
  82. /// </summary>
  83. public override void OnStopServer()
  84. {
  85. NetworkServer.SendToAll(new SceneMessage { sceneName = gameScene, sceneOperation = SceneOperation.UnloadAdditive });
  86. StartCoroutine(ServerUnloadSubScenes());
  87. clientIndex = 0;
  88. }
  89. // Unload the subScenes and unused assets and clear the subScenes list.
  90. IEnumerator ServerUnloadSubScenes()
  91. {
  92. for (int index = 0; index < subScenes.Count; index++)
  93. yield return SceneManager.UnloadSceneAsync(subScenes[index]);
  94. subScenes.Clear();
  95. subscenesLoaded = false;
  96. yield return Resources.UnloadUnusedAssets();
  97. }
  98. /// <summary>
  99. /// This is called when a client is stopped.
  100. /// </summary>
  101. public override void OnStopClient()
  102. {
  103. // make sure we're not in host mode
  104. if (mode == NetworkManagerMode.ClientOnly)
  105. StartCoroutine(ClientUnloadSubScenes());
  106. }
  107. // Unload all but the active scene, which is the "container" scene
  108. IEnumerator ClientUnloadSubScenes()
  109. {
  110. for (int index = 0; index < SceneManager.sceneCount; index++)
  111. {
  112. if (SceneManager.GetSceneAt(index) != SceneManager.GetActiveScene())
  113. yield return SceneManager.UnloadSceneAsync(SceneManager.GetSceneAt(index));
  114. }
  115. }
  116. #endregion
  117. }
  118. }