NetworkManager.cs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using kcp2k;
  5. using UnityEngine;
  6. using UnityEngine.SceneManagement;
  7. using UnityEngine.Serialization;
  8. namespace Mirror
  9. {
  10. public enum PlayerSpawnMethod { Random, RoundRobin }
  11. public enum NetworkManagerMode { Offline, ServerOnly, ClientOnly, Host }
  12. [DisallowMultipleComponent]
  13. [AddComponentMenu("Network/NetworkManager")]
  14. [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-manager")]
  15. public class NetworkManager : MonoBehaviour
  16. {
  17. /// <summary>Enable to keep NetworkManager alive when changing scenes.</summary>
  18. // This should be set if your game has a single NetworkManager that exists for the lifetime of the process. If there is a NetworkManager in each scene, then this should not be set.</para>
  19. [Header("Configuration")]
  20. [FormerlySerializedAs("m_DontDestroyOnLoad")]
  21. [Tooltip("Should the Network Manager object be persisted through scene changes?")]
  22. public bool dontDestroyOnLoad = true;
  23. // Deprecated 2021-03-10
  24. // Temporary bool to allow Network Manager to persist to offline scene
  25. // Based on Discord convo, BigBox is invoking StopHost in startup sequence, bouncing the server and clients back to offline scene, which resets Network Manager.
  26. // Request is for a checkbox to persist Network Manager to offline scene, despite the collision and warning.
  27. [Obsolete("This was added temporarily and will be removed in a future release.")]
  28. [Tooltip("Should the Network Manager object be persisted through scene change to the offline scene?")]
  29. public bool PersistNetworkManagerToOfflineScene;
  30. /// <summary>Multiplayer games should always run in the background so the network doesn't time out.</summary>
  31. [FormerlySerializedAs("m_RunInBackground")]
  32. [Tooltip("Multiplayer games should always run in the background so the network doesn't time out.")]
  33. public bool runInBackground = true;
  34. /// <summary>Should the server auto-start when 'Server Build' is checked in build settings</summary>
  35. [Tooltip("Should the server auto-start when 'Server Build' is checked in build settings")]
  36. [FormerlySerializedAs("startOnHeadless")]
  37. public bool autoStartServerBuild = true;
  38. /// <summary>Server Update frequency, per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE.</summary>
  39. [Tooltip("Server Update frequency, per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE.")]
  40. public int serverTickRate = 30;
  41. /// <summary>Automatically switch to this scene upon going offline (on start / on disconnect / on shutdown).</summary>
  42. [Header("Scene Management")]
  43. [Scene]
  44. [FormerlySerializedAs("m_OfflineScene")]
  45. [Tooltip("Scene that Mirror will switch to when the client or server is stopped")]
  46. public string offlineScene = "";
  47. /// <summary>Automatically switch to this scene upon going online (after connect/startserver).</summary>
  48. [Scene]
  49. [FormerlySerializedAs("m_OnlineScene")]
  50. [Tooltip("Scene that Mirror will switch to when the server is started. Clients will recieve a Scene Message to load the server's current scene when they connect.")]
  51. public string onlineScene = "";
  52. // transport layer
  53. [Header("Network Info")]
  54. [Tooltip("Transport component attached to this object that server and client will use to connect")]
  55. [SerializeField]
  56. protected Transport transport;
  57. /// <summary>Server's address for clients to connect to.</summary>
  58. [FormerlySerializedAs("m_NetworkAddress")]
  59. [Tooltip("Network Address where the client should connect to the server. Server does not use this for anything.")]
  60. public string networkAddress = "localhost";
  61. /// <summary>The maximum number of concurrent network connections to support.</summary>
  62. [FormerlySerializedAs("m_MaxConnections")]
  63. [Tooltip("Maximum number of concurrent connections.")]
  64. public int maxConnections = 100;
  65. // Deprecated 2021-05-10
  66. [Obsolete("Transport is responsible for timeouts.")]
  67. public bool disconnectInactiveConnections;
  68. // Deprecated 2021-05-10
  69. [Obsolete("Transport is responsible for timeouts. Configure the Transport's timeout setting instead.")]
  70. public float disconnectInactiveTimeout = 60f;
  71. [Header("Authentication")]
  72. [Tooltip("Authentication component attached to this object")]
  73. public NetworkAuthenticator authenticator;
  74. /// <summary>The default prefab to be used to create player objects on the server.</summary>
  75. // Player objects are created in the default handler for AddPlayer() on
  76. // the server. Implementing OnServerAddPlayer overrides this behaviour.
  77. [Header("Player Object")]
  78. [FormerlySerializedAs("m_PlayerPrefab")]
  79. [Tooltip("Prefab of the player object. Prefab must have a Network Identity component. May be an empty game object or a full avatar.")]
  80. public GameObject playerPrefab;
  81. /// <summary>Enable to automatically create player objects on connect and on scene change.</summary>
  82. [FormerlySerializedAs("m_AutoCreatePlayer")]
  83. [Tooltip("Should Mirror automatically spawn the player after scene change?")]
  84. public bool autoCreatePlayer = true;
  85. /// <summary>Where to spawn players.</summary>
  86. [FormerlySerializedAs("m_PlayerSpawnMethod")]
  87. [Tooltip("Round Robin or Random order of Start Position selection")]
  88. public PlayerSpawnMethod playerSpawnMethod;
  89. /// <summary>Prefabs that can be spawned over the network need to be registered here.</summary>
  90. [FormerlySerializedAs("m_SpawnPrefabs"), HideInInspector]
  91. public List<GameObject> spawnPrefabs = new List<GameObject>();
  92. /// <summary>List of transforms populated by NetworkStartPositions</summary>
  93. public static List<Transform> startPositions = new List<Transform>();
  94. public static int startPositionIndex;
  95. /// <summary>The one and only NetworkManager</summary>
  96. public static NetworkManager singleton { get; private set; }
  97. /// <summary>Number of active player objects across all connections on the server.</summary>
  98. public int numPlayers => NetworkServer.connections.Count(kv => kv.Value.identity != null);
  99. /// <summary>True if the server is running or client is connected/connecting.</summary>
  100. public bool isNetworkActive => NetworkServer.active || NetworkClient.active;
  101. // TODO remove this
  102. static NetworkConnection clientReadyConnection;
  103. /// <summary>True if the client loaded a new scene when connecting to the server.</summary>
  104. // This is set before OnClientConnect is called, so it can be checked
  105. // there to perform different logic if a scene load occurred.
  106. [NonSerialized]
  107. public bool clientLoadedScene;
  108. // helper enum to know if we started the networkmanager as server/client/host.
  109. // -> this is necessary because when StartHost changes server scene to
  110. // online scene, FinishLoadScene is called and the host client isn't
  111. // connected yet (no need to connect it before server was fully set up).
  112. // in other words, we need this to know which mode we are running in
  113. // during FinishLoadScene.
  114. public NetworkManagerMode mode { get; private set; }
  115. // virtual so that inheriting classes' OnValidate() can call base.OnValidate() too
  116. public virtual void OnValidate()
  117. {
  118. // add transport if there is none yet. makes upgrading easier.
  119. if (transport == null)
  120. {
  121. // was a transport added yet? if not, add one
  122. transport = GetComponent<Transport>();
  123. if (transport == null)
  124. {
  125. transport = gameObject.AddComponent<KcpTransport>();
  126. Debug.Log("NetworkManager: added default Transport because there was none yet.");
  127. }
  128. #if UNITY_EDITOR
  129. // For some insane reason, this line fails when building unless wrapped in this define. Stupid but true.
  130. // error CS0234: The type or namespace name 'Undo' does not exist in the namespace 'UnityEditor' (are you missing an assembly reference?)
  131. UnityEditor.Undo.RecordObject(gameObject, "Added default Transport");
  132. #endif
  133. }
  134. // always >= 0
  135. maxConnections = Mathf.Max(maxConnections, 0);
  136. if (playerPrefab != null && playerPrefab.GetComponent<NetworkIdentity>() == null)
  137. {
  138. Debug.LogError("NetworkManager - Player Prefab must have a NetworkIdentity.");
  139. playerPrefab = null;
  140. }
  141. // This avoids the mysterious "Replacing existing prefab with assetId ... Old prefab 'Player', New prefab 'Player'" warning.
  142. if (playerPrefab != null && spawnPrefabs.Contains(playerPrefab))
  143. {
  144. Debug.LogWarning("NetworkManager - Player Prefab should not be added to Registered Spawnable Prefabs list...removed it.");
  145. spawnPrefabs.Remove(playerPrefab);
  146. }
  147. }
  148. // virtual so that inheriting classes' Awake() can call base.Awake() too
  149. public virtual void Awake()
  150. {
  151. // Don't allow collision-destroyed second instance to continue.
  152. if (!InitializeSingleton()) return;
  153. Debug.Log("Mirror | mirror-networking.com | discord.gg/N9QVxbM");
  154. // Set the networkSceneName to prevent a scene reload
  155. // if client connection to server fails.
  156. networkSceneName = offlineScene;
  157. // setup OnSceneLoaded callback
  158. SceneManager.sceneLoaded += OnSceneLoaded;
  159. }
  160. // virtual so that inheriting classes' Start() can call base.Start() too
  161. public virtual void Start()
  162. {
  163. // headless mode? then start the server
  164. // can't do this in Awake because Awake is for initialization.
  165. // some transports might not be ready until Start.
  166. //
  167. // (tick rate is applied in StartServer!)
  168. #if UNITY_SERVER
  169. if (autoStartServerBuild)
  170. {
  171. StartServer();
  172. }
  173. #endif
  174. }
  175. // virtual so that inheriting classes' LateUpdate() can call base.LateUpdate() too
  176. public virtual void LateUpdate()
  177. {
  178. UpdateScene();
  179. }
  180. // keep the online scene change check in a separate function
  181. bool IsServerOnlineSceneChangeNeeded()
  182. {
  183. // Only change scene if the requested online scene is not blank, and is not already loaded
  184. return !string.IsNullOrEmpty(onlineScene) && !IsSceneActive(onlineScene) && onlineScene != offlineScene;
  185. }
  186. public static bool IsSceneActive(string scene)
  187. {
  188. Scene activeScene = SceneManager.GetActiveScene();
  189. return activeScene.path == scene || activeScene.name == scene;
  190. }
  191. // full server setup code, without spawning objects yet
  192. void SetupServer()
  193. {
  194. // Debug.Log("NetworkManager SetupServer");
  195. InitializeSingleton();
  196. if (runInBackground)
  197. Application.runInBackground = true;
  198. if (authenticator != null)
  199. {
  200. authenticator.OnStartServer();
  201. authenticator.OnServerAuthenticated.AddListener(OnServerAuthenticated);
  202. }
  203. ConfigureHeadlessFrameRate();
  204. // Copy auto-disconnect settings to NetworkServer
  205. #pragma warning disable 618
  206. NetworkServer.disconnectInactiveTimeout = disconnectInactiveTimeout;
  207. NetworkServer.disconnectInactiveConnections = disconnectInactiveConnections;
  208. #pragma warning restore 618
  209. // start listening to network connections
  210. NetworkServer.Listen(maxConnections);
  211. // call OnStartServer AFTER Listen, so that NetworkServer.active is
  212. // true and we can call NetworkServer.Spawn in OnStartServer
  213. // overrides.
  214. // (useful for loading & spawning stuff from database etc.)
  215. //
  216. // note: there is no risk of someone connecting after Listen() and
  217. // before OnStartServer() because this all runs in one thread
  218. // and we don't start processing connects until Update.
  219. OnStartServer();
  220. // this must be after Listen(), since that registers the default message handlers
  221. RegisterServerMessages();
  222. }
  223. /// <summary>Starts the server, listening for incoming connections.</summary>
  224. public void StartServer()
  225. {
  226. if (NetworkServer.active)
  227. {
  228. Debug.LogWarning("Server already started.");
  229. return;
  230. }
  231. mode = NetworkManagerMode.ServerOnly;
  232. // StartServer is inherently ASYNCHRONOUS (=doesn't finish immediately)
  233. //
  234. // Here is what it does:
  235. // Listen
  236. // if onlineScene:
  237. // LoadSceneAsync
  238. // ...
  239. // FinishLoadSceneServerOnly
  240. // SpawnObjects
  241. // else:
  242. // SpawnObjects
  243. //
  244. // there is NO WAY to make it synchronous because both LoadSceneAsync
  245. // and LoadScene do not finish loading immediately. as long as we
  246. // have the onlineScene feature, it will be asynchronous!
  247. SetupServer();
  248. // scene change needed? then change scene and spawn afterwards.
  249. if (IsServerOnlineSceneChangeNeeded())
  250. {
  251. ServerChangeScene(onlineScene);
  252. }
  253. // otherwise spawn directly
  254. else
  255. {
  256. NetworkServer.SpawnObjects();
  257. }
  258. }
  259. /// <summary>Starts the client, connects it to the server with networkAddress.</summary>
  260. public void StartClient()
  261. {
  262. if (NetworkClient.active)
  263. {
  264. Debug.LogWarning("Client already started.");
  265. return;
  266. }
  267. mode = NetworkManagerMode.ClientOnly;
  268. InitializeSingleton();
  269. if (runInBackground)
  270. Application.runInBackground = true;
  271. if (authenticator != null)
  272. {
  273. authenticator.OnStartClient();
  274. authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated);
  275. }
  276. // In case this is a headless client...
  277. ConfigureHeadlessFrameRate();
  278. RegisterClientMessages();
  279. if (string.IsNullOrEmpty(networkAddress))
  280. {
  281. Debug.LogError("Must set the Network Address field in the manager");
  282. return;
  283. }
  284. // Debug.Log("NetworkManager StartClient address:" + networkAddress);
  285. NetworkClient.Connect(networkAddress);
  286. OnStartClient();
  287. }
  288. /// <summary>Starts the client, connects it to the server via Uri</summary>
  289. public void StartClient(Uri uri)
  290. {
  291. if (NetworkClient.active)
  292. {
  293. Debug.LogWarning("Client already started.");
  294. return;
  295. }
  296. mode = NetworkManagerMode.ClientOnly;
  297. InitializeSingleton();
  298. if (runInBackground)
  299. Application.runInBackground = true;
  300. if (authenticator != null)
  301. {
  302. authenticator.OnStartClient();
  303. authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated);
  304. }
  305. RegisterClientMessages();
  306. // Debug.Log("NetworkManager StartClient address:" + uri);
  307. networkAddress = uri.Host;
  308. NetworkClient.Connect(uri);
  309. OnStartClient();
  310. }
  311. /// <summary>Starts a network "host" - a server and client in the same application.</summary>
  312. public void StartHost()
  313. {
  314. if (NetworkServer.active || NetworkClient.active)
  315. {
  316. Debug.LogWarning("Server or Client already started.");
  317. return;
  318. }
  319. mode = NetworkManagerMode.Host;
  320. // StartHost is inherently ASYNCHRONOUS (=doesn't finish immediately)
  321. //
  322. // Here is what it does:
  323. // Listen
  324. // ConnectHost
  325. // if onlineScene:
  326. // LoadSceneAsync
  327. // ...
  328. // FinishLoadSceneHost
  329. // FinishStartHost
  330. // SpawnObjects
  331. // StartHostClient <= not guaranteed to happen after SpawnObjects if onlineScene is set!
  332. // ClientAuth
  333. // success: server sends changescene msg to client
  334. // else:
  335. // FinishStartHost
  336. //
  337. // there is NO WAY to make it synchronous because both LoadSceneAsync
  338. // and LoadScene do not finish loading immediately. as long as we
  339. // have the onlineScene feature, it will be asynchronous!
  340. // setup server first
  341. SetupServer();
  342. // call OnStartHost AFTER SetupServer. this way we can use
  343. // NetworkServer.Spawn etc. in there too. just like OnStartServer
  344. // is called after the server is actually properly started.
  345. OnStartHost();
  346. // scene change needed? then change scene and spawn afterwards.
  347. // => BEFORE host client connects. if client auth succeeds then the
  348. // server tells it to load 'onlineScene'. we can't do that if
  349. // server is still in 'offlineScene'. so load on server first.
  350. if (IsServerOnlineSceneChangeNeeded())
  351. {
  352. // call FinishStartHost after changing scene.
  353. finishStartHostPending = true;
  354. ServerChangeScene(onlineScene);
  355. }
  356. // otherwise call FinishStartHost directly
  357. else
  358. {
  359. FinishStartHost();
  360. }
  361. }
  362. // This may be set true in StartHost and is evaluated in FinishStartHost
  363. bool finishStartHostPending;
  364. // FinishStartHost is guaranteed to be called after the host server was
  365. // fully started and all the asynchronous StartHost magic is finished
  366. // (= scene loading), or immediately if there was no asynchronous magic.
  367. //
  368. // note: we don't really need FinishStartClient/FinishStartServer. the
  369. // host version is enough.
  370. void FinishStartHost()
  371. {
  372. // ConnectHost needs to be called BEFORE SpawnObjects:
  373. // https://github.com/vis2k/Mirror/pull/1249/
  374. // -> this sets NetworkServer.localConnection.
  375. // -> localConnection needs to be set before SpawnObjects because:
  376. // -> SpawnObjects calls OnStartServer in all NetworkBehaviours
  377. // -> OnStartServer might spawn an object and set [SyncVar(hook="OnColorChanged")] object.color = green;
  378. // -> this calls SyncVar.set (generated by Weaver), which has
  379. // a custom case for host mode (because host mode doesn't
  380. // get OnDeserialize calls, where SyncVar hooks are usually
  381. // called):
  382. //
  383. // if (!SyncVarEqual(value, ref color))
  384. // {
  385. // if (NetworkServer.localClientActive && !getSyncVarHookGuard(1uL))
  386. // {
  387. // setSyncVarHookGuard(1uL, value: true);
  388. // OnColorChangedHook(value);
  389. // setSyncVarHookGuard(1uL, value: false);
  390. // }
  391. // SetSyncVar(value, ref color, 1uL);
  392. // }
  393. //
  394. // -> localClientActive needs to be true, otherwise the hook
  395. // isn't called in host mode!
  396. //
  397. // TODO call this after spawnobjects and worry about the syncvar hook fix later?
  398. NetworkClient.ConnectHost();
  399. // server scene was loaded. now spawn all the objects
  400. NetworkServer.SpawnObjects();
  401. // connect client and call OnStartClient AFTER server scene was
  402. // loaded and all objects were spawned.
  403. // DO NOT do this earlier. it would cause race conditions where a
  404. // client will do things before the server is even fully started.
  405. //Debug.Log("StartHostClient called");
  406. StartHostClient();
  407. }
  408. void StartHostClient()
  409. {
  410. //Debug.Log("NetworkManager ConnectLocalClient");
  411. if (authenticator != null)
  412. {
  413. authenticator.OnStartClient();
  414. authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated);
  415. }
  416. networkAddress = "localhost";
  417. NetworkServer.ActivateHostScene();
  418. RegisterClientMessages();
  419. // ConnectLocalServer needs to be called AFTER RegisterClientMessages
  420. // (https://github.com/vis2k/Mirror/pull/1249/)
  421. NetworkClient.ConnectLocalServer();
  422. OnStartClient();
  423. }
  424. /// <summary>This stops both the client and the server that the manager is using.</summary>
  425. public void StopHost()
  426. {
  427. OnStopHost();
  428. // calling OnTransportDisconnected was needed to fix
  429. // https://github.com/vis2k/Mirror/issues/1515
  430. // so that the host client receives a DisconnectMessage
  431. // TODO reevaluate if this is still needed after all the disconnect
  432. // fixes, and try to put this into LocalConnection.Disconnect!
  433. NetworkServer.OnTransportDisconnected(NetworkConnection.LocalConnectionId);
  434. StopClient();
  435. StopServer();
  436. }
  437. /// <summary>Stops the server from listening and simulating the game.</summary>
  438. public void StopServer()
  439. {
  440. // return if already stopped to avoid recursion deadlock
  441. if (!NetworkServer.active)
  442. return;
  443. if (authenticator != null)
  444. {
  445. authenticator.OnServerAuthenticated.RemoveListener(OnServerAuthenticated);
  446. authenticator.OnStopServer();
  447. }
  448. // Get Network Manager out of DDOL before going to offline scene
  449. // to avoid collision and let a fresh Network Manager be created.
  450. // IMPORTANT: .gameObject can be null if StopClient is called from
  451. // OnApplicationQuit or from tests!
  452. #pragma warning disable 618
  453. if (gameObject != null && !PersistNetworkManagerToOfflineScene &&
  454. gameObject.scene.name == "DontDestroyOnLoad"
  455. && !string.IsNullOrEmpty(offlineScene)
  456. && SceneManager.GetActiveScene().path != offlineScene)
  457. SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetActiveScene());
  458. #pragma warning restore 618
  459. OnStopServer();
  460. //Debug.Log("NetworkManager StopServer");
  461. NetworkServer.Shutdown();
  462. // set offline mode BEFORE changing scene so that FinishStartScene
  463. // doesn't think we need initialize anything.
  464. mode = NetworkManagerMode.Offline;
  465. if (!string.IsNullOrEmpty(offlineScene))
  466. {
  467. ServerChangeScene(offlineScene);
  468. }
  469. startPositionIndex = 0;
  470. networkSceneName = "";
  471. }
  472. /// <summary>Stops and disconnects the client.</summary>
  473. public void StopClient()
  474. {
  475. if (authenticator != null)
  476. {
  477. authenticator.OnClientAuthenticated.RemoveListener(OnClientAuthenticated);
  478. authenticator.OnStopClient();
  479. }
  480. // Get Network Manager out of DDOL before going to offline scene
  481. // to avoid collision and let a fresh Network Manager be created.
  482. // IMPORTANT: .gameObject can be null if StopClient is called from
  483. // OnApplicationQuit or from tests!
  484. #pragma warning disable 618
  485. if (gameObject != null && !PersistNetworkManagerToOfflineScene &&
  486. gameObject.scene.name == "DontDestroyOnLoad"
  487. && !string.IsNullOrEmpty(offlineScene)
  488. && SceneManager.GetActiveScene().path != offlineScene)
  489. SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetActiveScene());
  490. #pragma warning restore 618
  491. OnStopClient();
  492. //Debug.Log("NetworkManager StopClient");
  493. // shutdown client
  494. NetworkClient.Disconnect();
  495. NetworkClient.Shutdown();
  496. // set offline mode BEFORE changing scene so that FinishStartScene
  497. // doesn't think we need initialize anything.
  498. mode = NetworkManagerMode.Offline;
  499. // If this is the host player, StopServer will already be changing scenes.
  500. // Check loadingSceneAsync to ensure we don't double-invoke the scene change.
  501. // Check if NetworkServer.active because we can get here via Disconnect before server has started to change scenes.
  502. if (!string.IsNullOrEmpty(offlineScene) && !IsSceneActive(offlineScene) && loadingSceneAsync == null && !NetworkServer.active)
  503. {
  504. ClientChangeScene(offlineScene, SceneOperation.Normal);
  505. }
  506. networkSceneName = "";
  507. }
  508. // called when quitting the application by closing the window / pressing
  509. // stop in the editor. virtual so that inheriting classes'
  510. // OnApplicationQuit() can call base.OnApplicationQuit() too
  511. public virtual void OnApplicationQuit()
  512. {
  513. // stop client first
  514. // (we want to send the quit packet to the server instead of waiting
  515. // for a timeout)
  516. if (NetworkClient.isConnected)
  517. {
  518. StopClient();
  519. //Debug.Log("OnApplicationQuit: stopped client");
  520. }
  521. // stop server after stopping client (for proper host mode stopping)
  522. if (NetworkServer.active)
  523. {
  524. StopServer();
  525. //Debug.Log("OnApplicationQuit: stopped server");
  526. }
  527. }
  528. // DEPRECATED 2021-07-21
  529. [Obsolete("Renamed to ConfigureHeadlessFrameRate()")]
  530. public virtual void ConfigureServerFrameRate() {}
  531. /// <summary>Set the frame rate for a headless builds. Override to disable or modify.</summary>
  532. // useful for dedicated servers.
  533. // useful for headless benchmark clients.
  534. public virtual void ConfigureHeadlessFrameRate()
  535. {
  536. #if UNITY_SERVER
  537. Application.targetFrameRate = serverTickRate;
  538. // Debug.Log("Server Tick Rate set to: " + Application.targetFrameRate + " Hz.");
  539. #endif
  540. // call the obsolete function in case someone did anything important
  541. #pragma warning disable 618
  542. ConfigureServerFrameRate();
  543. #pragma warning restore 618
  544. }
  545. bool InitializeSingleton()
  546. {
  547. if (singleton != null && singleton == this)
  548. return true;
  549. if (dontDestroyOnLoad)
  550. {
  551. if (singleton != null)
  552. {
  553. Debug.LogWarning("Multiple NetworkManagers detected in the scene. Only one NetworkManager can exist at a time. The duplicate NetworkManager will be destroyed.");
  554. Destroy(gameObject);
  555. // Return false to not allow collision-destroyed second instance to continue.
  556. return false;
  557. }
  558. //Debug.Log("NetworkManager created singleton (DontDestroyOnLoad)");
  559. singleton = this;
  560. if (Application.isPlaying) DontDestroyOnLoad(gameObject);
  561. }
  562. else
  563. {
  564. //Debug.Log("NetworkManager created singleton (ForScene)");
  565. singleton = this;
  566. }
  567. // set active transport AFTER setting singleton.
  568. // so only if we didn't destroy ourselves.
  569. Transport.activeTransport = transport;
  570. return true;
  571. }
  572. void RegisterServerMessages()
  573. {
  574. NetworkServer.OnConnectedEvent = OnServerConnectInternal;
  575. NetworkServer.OnDisconnectedEvent = OnServerDisconnect;
  576. NetworkServer.OnErrorEvent = OnServerError;
  577. NetworkServer.RegisterHandler<AddPlayerMessage>(OnServerAddPlayerInternal);
  578. // Network Server initially registers its own handler for this, so we replace it here.
  579. NetworkServer.ReplaceHandler<ReadyMessage>(OnServerReadyMessageInternal);
  580. }
  581. void RegisterClientMessages()
  582. {
  583. NetworkClient.OnConnectedEvent = OnClientConnectInternal;
  584. NetworkClient.OnDisconnectedEvent = OnClientDisconnectInternal;
  585. NetworkClient.OnErrorEvent = OnClientError;
  586. NetworkClient.RegisterHandler<NotReadyMessage>(OnClientNotReadyMessageInternal);
  587. NetworkClient.RegisterHandler<SceneMessage>(OnClientSceneInternal, false);
  588. if (playerPrefab != null)
  589. NetworkClient.RegisterPrefab(playerPrefab);
  590. foreach (GameObject prefab in spawnPrefabs.Where(t => t != null))
  591. NetworkClient.RegisterPrefab(prefab);
  592. }
  593. // This is the only way to clear the singleton, so another instance can be created.
  594. public static void Shutdown()
  595. {
  596. if (singleton == null)
  597. return;
  598. startPositions.Clear();
  599. startPositionIndex = 0;
  600. clientReadyConnection = null;
  601. singleton.StopHost();
  602. singleton = null;
  603. }
  604. // virtual so that inheriting classes' OnDestroy() can call base.OnDestroy() too
  605. public virtual void OnDestroy()
  606. {
  607. //Debug.Log("NetworkManager destroyed");
  608. }
  609. /// <summary>The name of the current network scene.</summary>
  610. // set by NetworkManager when changing the scene.
  611. // new clients will automatically load this scene.
  612. // Loading a scene manually won't set it.
  613. public static string networkSceneName { get; protected set; } = "";
  614. public static AsyncOperation loadingSceneAsync;
  615. /// <summary>Change the server scene and all client's scenes across the network.</summary>
  616. // Called automatically if onlineScene or offlineScene are set, but it
  617. // can be called from user code to switch scenes again while the game is
  618. // in progress. This automatically sets clients to be not-ready during
  619. // the change and ready again to participate in the new scene.
  620. public virtual void ServerChangeScene(string newSceneName)
  621. {
  622. if (string.IsNullOrEmpty(newSceneName))
  623. {
  624. Debug.LogError("ServerChangeScene empty scene name");
  625. return;
  626. }
  627. // Debug.Log("ServerChangeScene " + newSceneName);
  628. NetworkServer.SetAllClientsNotReady();
  629. networkSceneName = newSceneName;
  630. // Let server prepare for scene change
  631. OnServerChangeScene(newSceneName);
  632. // set server flag to stop processing messages while changing scenes
  633. // it will be re-enabled in FinishLoadScene.
  634. NetworkServer.isLoadingScene = true;
  635. loadingSceneAsync = SceneManager.LoadSceneAsync(newSceneName);
  636. // ServerChangeScene can be called when stopping the server
  637. // when this happens the server is not active so does not need to tell clients about the change
  638. if (NetworkServer.active)
  639. {
  640. // notify all clients about the new scene
  641. NetworkServer.SendToAll(new SceneMessage { sceneName = newSceneName });
  642. }
  643. startPositionIndex = 0;
  644. startPositions.Clear();
  645. }
  646. // This is only set in ClientChangeScene below...never on server.
  647. // We need to check this in OnClientSceneChanged called from FinishLoadSceneClientOnly
  648. // to prevent AddPlayer message after loading/unloading additive scenes
  649. SceneOperation clientSceneOperation = SceneOperation.Normal;
  650. internal void ClientChangeScene(string newSceneName, SceneOperation sceneOperation = SceneOperation.Normal, bool customHandling = false)
  651. {
  652. if (string.IsNullOrEmpty(newSceneName))
  653. {
  654. Debug.LogError("ClientChangeScene empty scene name");
  655. return;
  656. }
  657. // Debug.Log("ClientChangeScene newSceneName:" + newSceneName + " networkSceneName:" + networkSceneName);
  658. // Let client prepare for scene change
  659. OnClientChangeScene(newSceneName, sceneOperation, customHandling);
  660. // After calling OnClientChangeScene, exit if server since server is already doing
  661. // the actual scene change, and we don't need to do it for the host client
  662. if (NetworkServer.active)
  663. return;
  664. // set client flag to stop processing messages while loading scenes.
  665. // otherwise we would process messages and then lose all the state
  666. // as soon as the load is finishing, causing all kinds of bugs
  667. // because of missing state.
  668. // (client may be null after StopClient etc.)
  669. // Debug.Log("ClientChangeScene: pausing handlers while scene is loading to avoid data loss after scene was loaded.");
  670. NetworkClient.isLoadingScene = true;
  671. // Cache sceneOperation so we know what was requested by the
  672. // Scene message in OnClientChangeScene and OnClientSceneChanged
  673. clientSceneOperation = sceneOperation;
  674. // scene handling will happen in overrides of OnClientChangeScene and/or OnClientSceneChanged
  675. // Do not call FinishLoadScene here. Custom handler will assign loadingSceneAsync and we need
  676. // to wait for that to finish. UpdateScene already checks for that to be not null and isDone.
  677. if (customHandling)
  678. return;
  679. switch (sceneOperation)
  680. {
  681. case SceneOperation.Normal:
  682. loadingSceneAsync = SceneManager.LoadSceneAsync(newSceneName);
  683. break;
  684. case SceneOperation.LoadAdditive:
  685. // Ensure additive scene is not already loaded on client by name or path
  686. // since we don't know which was passed in the Scene message
  687. if (!SceneManager.GetSceneByName(newSceneName).IsValid() && !SceneManager.GetSceneByPath(newSceneName).IsValid())
  688. loadingSceneAsync = SceneManager.LoadSceneAsync(newSceneName, LoadSceneMode.Additive);
  689. else
  690. {
  691. Debug.LogWarning($"Scene {newSceneName} is already loaded");
  692. // Reset the flag that we disabled before entering this switch
  693. NetworkClient.isLoadingScene = false;
  694. }
  695. break;
  696. case SceneOperation.UnloadAdditive:
  697. // Ensure additive scene is actually loaded on client by name or path
  698. // since we don't know which was passed in the Scene message
  699. if (SceneManager.GetSceneByName(newSceneName).IsValid() || SceneManager.GetSceneByPath(newSceneName).IsValid())
  700. loadingSceneAsync = SceneManager.UnloadSceneAsync(newSceneName, UnloadSceneOptions.UnloadAllEmbeddedSceneObjects);
  701. else
  702. {
  703. Debug.LogWarning($"Cannot unload {newSceneName} with UnloadAdditive operation");
  704. // Reset the flag that we disabled before entering this switch
  705. NetworkClient.isLoadingScene = false;
  706. }
  707. break;
  708. }
  709. // don't change the client's current networkSceneName when loading additive scene content
  710. if (sceneOperation == SceneOperation.Normal)
  711. networkSceneName = newSceneName;
  712. }
  713. // support additive scene loads:
  714. // NetworkScenePostProcess disables all scene objects on load, and
  715. // * NetworkServer.SpawnObjects enables them again on the server when
  716. // calling OnStartServer
  717. // * NetworkClient.PrepareToSpawnSceneObjects enables them again on the
  718. // client after the server sends ObjectSpawnStartedMessage to client
  719. // in SpawnObserversForConnection. this is only called when the
  720. // client joins, so we need to rebuild scene objects manually again
  721. // TODO merge this with FinishLoadScene()?
  722. void OnSceneLoaded(Scene scene, LoadSceneMode mode)
  723. {
  724. if (mode == LoadSceneMode.Additive)
  725. {
  726. if (NetworkServer.active)
  727. {
  728. // TODO only respawn the server objects from that scene later!
  729. NetworkServer.SpawnObjects();
  730. // Debug.Log("Respawned Server objects after additive scene load: " + scene.name);
  731. }
  732. if (NetworkClient.active)
  733. {
  734. NetworkClient.PrepareToSpawnSceneObjects();
  735. // Debug.Log("Rebuild Client spawnableObjects after additive scene load: " + scene.name);
  736. }
  737. }
  738. }
  739. void UpdateScene()
  740. {
  741. if (loadingSceneAsync != null && loadingSceneAsync.isDone)
  742. {
  743. // Debug.Log("ClientChangeScene done readyCon:" + clientReadyConnection);
  744. // try-finally to guarantee loadingSceneAsync being cleared.
  745. // fixes https://github.com/vis2k/Mirror/issues/2517 where if
  746. // FinishLoadScene throws an exception, loadingSceneAsync would
  747. // never be cleared and this code would run every Update.
  748. try
  749. {
  750. FinishLoadScene();
  751. }
  752. finally
  753. {
  754. loadingSceneAsync.allowSceneActivation = true;
  755. loadingSceneAsync = null;
  756. }
  757. }
  758. }
  759. protected void FinishLoadScene()
  760. {
  761. // NOTE: this cannot use NetworkClient.allClients[0] - that client may be for a completely different purpose.
  762. // process queued messages that we received while loading the scene
  763. Debug.Log("FinishLoadScene: resuming handlers after scene was loading.");
  764. NetworkServer.isLoadingScene = false;
  765. NetworkClient.isLoadingScene = false;
  766. // host mode?
  767. if (mode == NetworkManagerMode.Host)
  768. {
  769. FinishLoadSceneHost();
  770. }
  771. // server-only mode?
  772. else if (mode == NetworkManagerMode.ServerOnly)
  773. {
  774. FinishLoadSceneServerOnly();
  775. }
  776. // client-only mode?
  777. else if (mode == NetworkManagerMode.ClientOnly)
  778. {
  779. FinishLoadSceneClientOnly();
  780. }
  781. // otherwise we called it after stopping when loading offline scene.
  782. // do nothing then.
  783. }
  784. // finish load scene part for host mode. makes code easier and is
  785. // necessary for FinishStartHost later.
  786. // (the 3 things have to happen in that exact order)
  787. void FinishLoadSceneHost()
  788. {
  789. // debug message is very important. if we ever break anything then
  790. // it's very obvious to notice.
  791. Debug.Log("Finished loading scene in host mode.");
  792. if (clientReadyConnection != null)
  793. {
  794. OnClientConnect(clientReadyConnection);
  795. clientLoadedScene = true;
  796. clientReadyConnection = null;
  797. }
  798. // do we need to finish a StartHost() call?
  799. // then call FinishStartHost and let it take care of spawning etc.
  800. if (finishStartHostPending)
  801. {
  802. finishStartHostPending = false;
  803. FinishStartHost();
  804. // call OnServerSceneChanged
  805. OnServerSceneChanged(networkSceneName);
  806. // DO NOT call OnClientSceneChanged here.
  807. // the scene change happened because StartHost loaded the
  808. // server's online scene. it has nothing to do with the client.
  809. // this was not meant as a client scene load, so don't call it.
  810. //
  811. // otherwise AddPlayer would be called twice:
  812. // -> once for client OnConnected
  813. // -> once in OnClientSceneChanged
  814. }
  815. // otherwise we just changed a scene in host mode
  816. else
  817. {
  818. // spawn server objects
  819. NetworkServer.SpawnObjects();
  820. // call OnServerSceneChanged
  821. OnServerSceneChanged(networkSceneName);
  822. if (NetworkClient.isConnected)
  823. {
  824. // let client know that we changed scene
  825. OnClientSceneChanged(NetworkClient.connection);
  826. }
  827. }
  828. }
  829. // finish load scene part for server-only. . makes code easier and is
  830. // necessary for FinishStartServer later.
  831. void FinishLoadSceneServerOnly()
  832. {
  833. // debug message is very important. if we ever break anything then
  834. // it's very obvious to notice.
  835. Debug.Log("Finished loading scene in server-only mode.");
  836. NetworkServer.SpawnObjects();
  837. OnServerSceneChanged(networkSceneName);
  838. }
  839. // finish load scene part for client-only. makes code easier and is
  840. // necessary for FinishStartClient later.
  841. void FinishLoadSceneClientOnly()
  842. {
  843. // debug message is very important. if we ever break anything then
  844. // it's very obvious to notice.
  845. Debug.Log("Finished loading scene in client-only mode.");
  846. if (clientReadyConnection != null)
  847. {
  848. OnClientConnect(clientReadyConnection);
  849. clientLoadedScene = true;
  850. clientReadyConnection = null;
  851. }
  852. if (NetworkClient.isConnected)
  853. {
  854. OnClientSceneChanged(NetworkClient.connection);
  855. }
  856. }
  857. /// <summary>
  858. /// Registers the transform of a game object as a player spawn location.
  859. /// <para>This is done automatically by NetworkStartPosition components, but can be done manually from user script code.</para>
  860. /// </summary>
  861. /// <param name="start">Transform to register.</param>
  862. public static void RegisterStartPosition(Transform start)
  863. {
  864. // Debug.Log("RegisterStartPosition: (" + start.gameObject.name + ") " + start.position);
  865. startPositions.Add(start);
  866. // reorder the list so that round-robin spawning uses the start positions
  867. // in hierarchy order. This assumes all objects with NetworkStartPosition
  868. // component are siblings, either in the scene root or together as children
  869. // under a single parent in the scene.
  870. startPositions = startPositions.OrderBy(transform => transform.GetSiblingIndex()).ToList();
  871. }
  872. /// <summary>Unregister a Transform from start positions.</summary>
  873. // TODO why is this static?
  874. public static void UnRegisterStartPosition(Transform start)
  875. {
  876. // Debug.Log("UnRegisterStartPosition: (" + start.gameObject.name + ") " + start.position);
  877. startPositions.Remove(start);
  878. }
  879. /// <summary>Get the next NetworkStartPosition based on the selected PlayerSpawnMethod.</summary>
  880. public Transform GetStartPosition()
  881. {
  882. // first remove any dead transforms
  883. startPositions.RemoveAll(t => t == null);
  884. if (startPositions.Count == 0)
  885. return null;
  886. if (playerSpawnMethod == PlayerSpawnMethod.Random)
  887. {
  888. return startPositions[UnityEngine.Random.Range(0, startPositions.Count)];
  889. }
  890. else
  891. {
  892. Transform startPosition = startPositions[startPositionIndex];
  893. startPositionIndex = (startPositionIndex + 1) % startPositions.Count;
  894. return startPosition;
  895. }
  896. }
  897. void OnServerConnectInternal(NetworkConnection conn)
  898. {
  899. //Debug.Log("NetworkManager.OnServerConnectInternal");
  900. if (authenticator != null)
  901. {
  902. // we have an authenticator - let it handle authentication
  903. authenticator.OnServerAuthenticate(conn);
  904. }
  905. else
  906. {
  907. // authenticate immediately
  908. OnServerAuthenticated(conn);
  909. }
  910. }
  911. // called after successful authentication
  912. // TODO do the NetworkServer.OnAuthenticated thing from x branch
  913. void OnServerAuthenticated(NetworkConnection conn)
  914. {
  915. //Debug.Log("NetworkManager.OnServerAuthenticated");
  916. // set connection to authenticated
  917. conn.isAuthenticated = true;
  918. // proceed with the login handshake by calling OnServerConnect
  919. if (networkSceneName != "" && networkSceneName != offlineScene)
  920. {
  921. SceneMessage msg = new SceneMessage() { sceneName = networkSceneName };
  922. conn.Send(msg);
  923. }
  924. OnServerConnect(conn);
  925. }
  926. void OnServerReadyMessageInternal(NetworkConnection conn, ReadyMessage msg)
  927. {
  928. //Debug.Log("NetworkManager.OnServerReadyMessageInternal");
  929. OnServerReady(conn);
  930. }
  931. void OnServerAddPlayerInternal(NetworkConnection conn, AddPlayerMessage msg)
  932. {
  933. //Debug.Log("NetworkManager.OnServerAddPlayer");
  934. if (autoCreatePlayer && playerPrefab == null)
  935. {
  936. Debug.LogError("The PlayerPrefab is empty on the NetworkManager. Please setup a PlayerPrefab object.");
  937. return;
  938. }
  939. if (autoCreatePlayer && playerPrefab.GetComponent<NetworkIdentity>() == null)
  940. {
  941. Debug.LogError("The PlayerPrefab does not have a NetworkIdentity. Please add a NetworkIdentity to the player prefab.");
  942. return;
  943. }
  944. if (conn.identity != null)
  945. {
  946. Debug.LogError("There is already a player for this connection.");
  947. return;
  948. }
  949. OnServerAddPlayer(conn);
  950. }
  951. void OnClientConnectInternal()
  952. {
  953. //Debug.Log("NetworkManager.OnClientConnectInternal");
  954. if (authenticator != null)
  955. {
  956. // we have an authenticator - let it handle authentication
  957. authenticator.OnClientAuthenticate();
  958. }
  959. else
  960. {
  961. // authenticate immediately
  962. OnClientAuthenticated(NetworkClient.connection);
  963. }
  964. }
  965. // called after successful authentication
  966. void OnClientAuthenticated(NetworkConnection conn)
  967. {
  968. //Debug.Log("NetworkManager.OnClientAuthenticated");
  969. // set connection to authenticated
  970. conn.isAuthenticated = true;
  971. // proceed with the login handshake by calling OnClientConnect
  972. if (string.IsNullOrEmpty(onlineScene) || onlineScene == offlineScene || IsSceneActive(onlineScene))
  973. {
  974. clientLoadedScene = false;
  975. OnClientConnect(conn);
  976. }
  977. else
  978. {
  979. // will wait for scene id to come from the server.
  980. clientLoadedScene = true;
  981. clientReadyConnection = conn;
  982. }
  983. }
  984. // TODO call OnClientDisconnect directly, don't pass the connection
  985. void OnClientDisconnectInternal()
  986. {
  987. //Debug.Log("NetworkManager.OnClientDisconnectInternal");
  988. OnClientDisconnect(NetworkClient.connection);
  989. }
  990. void OnClientNotReadyMessageInternal(NotReadyMessage msg)
  991. {
  992. //Debug.Log("NetworkManager.OnClientNotReadyMessageInternal");
  993. NetworkClient.ready = false;
  994. OnClientNotReady(NetworkClient.connection);
  995. // NOTE: clientReadyConnection is not set here! don't want OnClientConnect to be invoked again after scene changes.
  996. }
  997. void OnClientSceneInternal(SceneMessage msg)
  998. {
  999. //Debug.Log("NetworkManager.OnClientSceneInternal");
  1000. // This needs to run for host client too. NetworkServer.active is checked there
  1001. if (NetworkClient.isConnected)
  1002. {
  1003. ClientChangeScene(msg.sceneName, msg.sceneOperation, msg.customHandling);
  1004. }
  1005. }
  1006. /// <summary>Called on the server when a new client connects.</summary>
  1007. public virtual void OnServerConnect(NetworkConnection conn) {}
  1008. /// <summary>Called on the server when a client disconnects.</summary>
  1009. // Called by NetworkServer.OnTransportDisconnect!
  1010. public virtual void OnServerDisconnect(NetworkConnection conn)
  1011. {
  1012. // by default, this function destroys the connection's player.
  1013. // can be overwritten for cases like delayed logouts in MMOs to
  1014. // avoid players escaping from PvP situations by logging out.
  1015. NetworkServer.DestroyPlayerForConnection(conn);
  1016. //Debug.Log("OnServerDisconnect: Client disconnected.");
  1017. }
  1018. /// <summary>Called on the server when a client is ready (= loaded the scene)</summary>
  1019. public virtual void OnServerReady(NetworkConnection conn)
  1020. {
  1021. if (conn.identity == null)
  1022. {
  1023. // this is now allowed (was not for a while)
  1024. //Debug.Log("Ready with no player object");
  1025. }
  1026. NetworkServer.SetClientReady(conn);
  1027. }
  1028. /// <summary>Called on server when a client requests to add the player. Adds playerPrefab by default. Can be overwritten.</summary>
  1029. // The default implementation for this function creates a new player object from the playerPrefab.
  1030. public virtual void OnServerAddPlayer(NetworkConnection conn)
  1031. {
  1032. Transform startPos = GetStartPosition();
  1033. GameObject player = startPos != null
  1034. ? Instantiate(playerPrefab, startPos.position, startPos.rotation)
  1035. : Instantiate(playerPrefab);
  1036. // instantiating a "Player" prefab gives it the name "Player(clone)"
  1037. // => appending the connectionId is WAY more useful for debugging!
  1038. player.name = $"{playerPrefab.name} [connId={conn.connectionId}]";
  1039. NetworkServer.AddPlayerForConnection(conn, player);
  1040. }
  1041. // Deprecated 2021-02-13
  1042. [Obsolete("OnServerError was removed because it hasn't been used in a long time.")]
  1043. public virtual void OnServerError(NetworkConnection conn, int errorCode) {}
  1044. /// <summary>Called on server when transport raises an exception. NetworkConnection may be null.</summary>
  1045. public virtual void OnServerError(NetworkConnection conn, Exception exception) {}
  1046. /// <summary>Called from ServerChangeScene immediately before SceneManager.LoadSceneAsync is executed</summary>
  1047. public virtual void OnServerChangeScene(string newSceneName) {}
  1048. /// <summary>Called on server after a scene load with ServerChangeScene() is completed.</summary>
  1049. public virtual void OnServerSceneChanged(string sceneName) {}
  1050. /// <summary>Called on the client when connected to a server. By default it sets client as ready and adds a player.</summary>
  1051. // TODO client only ever uses NetworkClient.connection. this parameter is redundant.
  1052. public virtual void OnClientConnect(NetworkConnection conn)
  1053. {
  1054. // OnClientConnect by default calls AddPlayer but it should not do
  1055. // that when we have online/offline scenes. so we need the
  1056. // clientLoadedScene flag to prevent it.
  1057. if (!clientLoadedScene)
  1058. {
  1059. // Ready/AddPlayer is usually triggered by a scene load
  1060. // completing. if no scene was loaded, then Ready/AddPlayer it
  1061. // here instead.
  1062. if (!NetworkClient.ready) NetworkClient.Ready();
  1063. if (autoCreatePlayer)
  1064. {
  1065. NetworkClient.AddPlayer();
  1066. }
  1067. }
  1068. }
  1069. /// <summary>Called on clients when disconnected from a server.</summary>
  1070. // TODO client only ever uses NetworkClient.connection. this parameter is redundant.
  1071. public virtual void OnClientDisconnect(NetworkConnection conn)
  1072. {
  1073. StopClient();
  1074. }
  1075. // Deprecated 2021-02-13
  1076. [Obsolete("OnClientError was removed because it hasn't been used in a long time.")]
  1077. public virtual void OnClientError(NetworkConnection conn, int errorCode) {}
  1078. /// <summary>Called on client when transport raises an exception.</summary>
  1079. public virtual void OnClientError(Exception exception) {}
  1080. /// <summary>Called on clients when a servers tells the client it is no longer ready, e.g. when switching scenes.</summary>
  1081. // TODO client only ever uses NetworkClient.connection. this parameter is redundant.
  1082. public virtual void OnClientNotReady(NetworkConnection conn) {}
  1083. /// <summary>Called from ClientChangeScene immediately before SceneManager.LoadSceneAsync is executed</summary>
  1084. // customHandling: indicates if scene loading will be handled through overrides
  1085. public virtual void OnClientChangeScene(string newSceneName, SceneOperation sceneOperation, bool customHandling) {}
  1086. /// <summary>Called on clients when a scene has completed loaded, when the scene load was initiated by the server.</summary>
  1087. // Scene changes can cause player objects to be destroyed. The default
  1088. // implementation of OnClientSceneChanged in the NetworkManager is to
  1089. // add a player object for the connection if no player object exists.
  1090. // TODO client only ever uses NetworkClient.connection. this parameter is redundant.
  1091. public virtual void OnClientSceneChanged(NetworkConnection conn)
  1092. {
  1093. // always become ready.
  1094. if (!NetworkClient.ready) NetworkClient.Ready();
  1095. // Only call AddPlayer for normal scene changes, not additive load/unload
  1096. if (clientSceneOperation == SceneOperation.Normal && autoCreatePlayer && NetworkClient.localPlayer == null)
  1097. {
  1098. // add player if existing one is null
  1099. NetworkClient.AddPlayer();
  1100. }
  1101. }
  1102. // Since there are multiple versions of StartServer, StartClient and
  1103. // StartHost, to reliably customize their functionality, users would
  1104. // need override all the versions. Instead these callbacks are invoked
  1105. // from all versions, so users only need to implement this one case.
  1106. /// <summary>This is invoked when a host is started.</summary>
  1107. public virtual void OnStartHost() {}
  1108. /// <summary>This is invoked when a server is started - including when a host is started.</summary>
  1109. public virtual void OnStartServer() {}
  1110. /// <summary>This is invoked when the client is started.</summary>
  1111. public virtual void OnStartClient() {}
  1112. /// <summary>This is called when a server is stopped - including when a host is stopped.</summary>
  1113. public virtual void OnStopServer() {}
  1114. /// <summary>This is called when a client is stopped.</summary>
  1115. public virtual void OnStopClient() {}
  1116. /// <summary>This is called when a host is stopped.</summary>
  1117. public virtual void OnStopHost() {}
  1118. }
  1119. }