NetworkIdentity.cs 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285
  1. using System;
  2. using System.Collections.Generic;
  3. using Mirror.RemoteCalls;
  4. using UnityEngine;
  5. using UnityEngine.Serialization;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. #if UNITY_2021_2_OR_NEWER
  9. using UnityEditor.SceneManagement;
  10. #elif UNITY_2018_3_OR_NEWER
  11. using UnityEditor.Experimental.SceneManagement;
  12. #endif
  13. #endif
  14. namespace Mirror
  15. {
  16. // Default = use interest management
  17. // ForceHidden = useful to hide monsters while they respawn etc.
  18. // ForceShown = useful to have score NetworkIdentities that always broadcast
  19. // to everyone etc.
  20. public enum Visibility { Default, ForceHidden, ForceShown }
  21. public struct NetworkIdentitySerialization
  22. {
  23. // IMPORTANT: int tick avoids floating point inaccuracy over days/weeks
  24. public int tick;
  25. public NetworkWriter ownerWriter;
  26. public NetworkWriter observersWriter;
  27. }
  28. /// <summary>NetworkIdentity identifies objects across the network.</summary>
  29. [DisallowMultipleComponent]
  30. // NetworkIdentity.Awake initializes all NetworkComponents.
  31. // let's make sure it's always called before their Awake's.
  32. [DefaultExecutionOrder(-1)]
  33. [AddComponentMenu("Network/NetworkIdentity")]
  34. [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-identity")]
  35. public sealed class NetworkIdentity : MonoBehaviour
  36. {
  37. /// <summary>Returns true if running as a client and this object was spawned by a server.</summary>
  38. //
  39. // IMPORTANT:
  40. // OnStartClient sets it to true. we NEVER set it to false after.
  41. // otherwise components like Skillbars couldn't use OnDestroy()
  42. // for saving, etc. since isClient may have been reset before
  43. // OnDestroy was called.
  44. //
  45. // we also DO NOT make it dependent on NetworkClient.active or similar.
  46. // we set it, then never change it. that's the user's expectation too.
  47. //
  48. // => fixes https://github.com/vis2k/Mirror/issues/1475
  49. public bool isClient { get; internal set; }
  50. /// <summary>Returns true if NetworkServer.active and server is not stopped.</summary>
  51. //
  52. // IMPORTANT:
  53. // OnStartServer sets it to true. we NEVER set it to false after.
  54. // otherwise components like Skillbars couldn't use OnDestroy()
  55. // for saving, etc. since isServer may have been reset before
  56. // OnDestroy was called.
  57. //
  58. // we also DO NOT make it dependent on NetworkServer.active or similar.
  59. // we set it, then never change it. that's the user's expectation too.
  60. //
  61. // => fixes https://github.com/vis2k/Mirror/issues/1484
  62. // => fixes https://github.com/vis2k/Mirror/issues/2533
  63. public bool isServer { get; internal set; }
  64. /// <summary>Return true if this object represents the player on the local machine.</summary>
  65. //
  66. // IMPORTANT:
  67. // OnStartLocalPlayer sets it to true. we NEVER set it to false after.
  68. // otherwise components like Skillbars couldn't use OnDestroy()
  69. // for saving, etc. since isLocalPlayer may have been reset before
  70. // OnDestroy was called.
  71. //
  72. // we also DO NOT make it dependent on NetworkClient.localPlayer or similar.
  73. // we set it, then never change it. that's the user's expectation too.
  74. //
  75. // => fixes https://github.com/vis2k/Mirror/issues/2615
  76. public bool isLocalPlayer { get; internal set; }
  77. /// <summary>True if this object only exists on the server</summary>
  78. public bool isServerOnly => isServer && !isClient;
  79. /// <summary>True if this object exists on a client that is not also acting as a server.</summary>
  80. public bool isClientOnly => isClient && !isServer;
  81. /// <summary>True if this object is the authoritative player object on the client.</summary>
  82. // Determined at runtime. For most objects, authority is held by the server.
  83. // For objects that had their authority set by AssignClientAuthority on
  84. // the server, this will be true on the client that owns the object. NOT
  85. // on other clients.
  86. public bool hasAuthority { get; internal set; }
  87. /// <summary>The set of network connections (players) that can see this object.</summary>
  88. // note: null until OnStartServer was called. this is necessary for
  89. // SendTo* to work properly in server-only mode.
  90. public Dictionary<int, NetworkConnection> observers;
  91. /// <summary>The unique network Id of this object (unique at runtime).</summary>
  92. public uint netId { get; internal set; }
  93. /// <summary>Unique identifier for NetworkIdentity objects within a scene, used for spawning scene objects.</summary>
  94. // persistent scene id <sceneHash/32,sceneId/32> (see AssignSceneID comments)
  95. [FormerlySerializedAs("m_SceneId"), HideInInspector]
  96. public ulong sceneId;
  97. /// <summary>Make this object only exist when the game is running as a server (or host).</summary>
  98. [FormerlySerializedAs("m_ServerOnly")]
  99. [Tooltip("Prevents this object from being spawned / enabled on clients")]
  100. public bool serverOnly;
  101. // Set before Destroy is called so that OnDestroy doesn't try to destroy
  102. // the object again
  103. internal bool destroyCalled;
  104. /// <summary>Client's network connection to the server. This is only valid for player objects on the client.</summary>
  105. public NetworkConnection connectionToServer { get; internal set; }
  106. /// <summary>Server's network connection to the client. This is only valid for client-owned objects (including the Player object) on the server.</summary>
  107. public NetworkConnectionToClient connectionToClient
  108. {
  109. get => _connectionToClient;
  110. internal set
  111. {
  112. _connectionToClient?.RemoveOwnedObject(this);
  113. _connectionToClient = value;
  114. _connectionToClient?.AddOwnedObject(this);
  115. }
  116. }
  117. NetworkConnectionToClient _connectionToClient;
  118. /// <summary>All spawned NetworkIdentities by netId. Available on server and client.</summary>
  119. // server sees ALL spawned ones.
  120. // client sees OBSERVED spawned ones.
  121. // => split into NetworkServer.spawned and NetworkClient.spawned to
  122. // reduce shared state between server & client.
  123. // => prepares for NetworkServer/Client as component & better host mode.
  124. [Obsolete("NetworkIdentity.spawned is now NetworkServer.spawned on server, NetworkClient.spawned on client.\nPrepares for NetworkServer/Client as component, better host mode, better testing.")]
  125. public static Dictionary<uint, NetworkIdentity> spawned
  126. {
  127. get
  128. {
  129. // server / host mode: use the one from server.
  130. // host mode has access to all spawned.
  131. if (NetworkServer.active) return NetworkServer.spawned;
  132. // client
  133. if (NetworkClient.active) return NetworkClient.spawned;
  134. // neither: then we are testing.
  135. // we could default to NetworkServer.spawned.
  136. // but from the outside, that's not obvious.
  137. // better to throw an exception to make it obvious.
  138. throw new Exception("NetworkIdentity.spawned was accessed before NetworkServer/NetworkClient were active.");
  139. }
  140. }
  141. // get all NetworkBehaviour components
  142. public NetworkBehaviour[] NetworkBehaviours { get; private set; }
  143. #pragma warning disable 618
  144. [Obsolete(NetworkVisibilityObsoleteMessage.Message)]
  145. public NetworkVisibility visibility { get; private set; }
  146. #pragma warning restore 618
  147. // current visibility
  148. //
  149. // Default = use interest management
  150. // ForceHidden = useful to hide monsters while they respawn etc.
  151. // ForceShown = useful to have score NetworkIdentities that always broadcast
  152. // to everyone etc.
  153. //
  154. // TODO rename to 'visibility' after removing .visibility some day!
  155. [Tooltip("Visibility can overwrite interest management. ForceHidden can be useful to hide monsters while they respawn. ForceShown can be useful for score NetworkIdentities that should always broadcast to everyone in the world.")]
  156. public Visibility visible = Visibility.Default;
  157. // broadcasting serializes all entities around a player for each player.
  158. // we don't want to serialize one entity twice in the same tick.
  159. // so we cache the last serialization and remember the timestamp so we
  160. // know which Update it was serialized.
  161. // (timestamp is the same while inside Update)
  162. // => this way we don't need to pool thousands of writers either.
  163. // => way easier to store them per object
  164. NetworkIdentitySerialization lastSerialization = new NetworkIdentitySerialization
  165. {
  166. ownerWriter = new NetworkWriter(),
  167. observersWriter = new NetworkWriter()
  168. };
  169. /// <summary>Prefab GUID used to spawn prefabs across the network.</summary>
  170. //
  171. // The AssetId trick:
  172. // Ideally we would have a serialized 'Guid m_AssetId' but Unity can't
  173. // serialize it because Guid's internal bytes are private
  174. //
  175. // UNET used 'NetworkHash128' originally, with byte0, ..., byte16
  176. // which works, but it just unnecessary extra code
  177. //
  178. // Using just the Guid string would work, but it's 32 chars long and
  179. // would then be sent over the network as 64 instead of 16 bytes
  180. //
  181. // => The solution is to serialize the string internally here and then
  182. // use the real 'Guid' type for everything else via .assetId
  183. public Guid assetId
  184. {
  185. get
  186. {
  187. #if UNITY_EDITOR
  188. // This is important because sometimes OnValidate does not run (like when adding view to prefab with no child links)
  189. if (string.IsNullOrEmpty(m_AssetId))
  190. SetupIDs();
  191. #endif
  192. // convert string to Guid and use .Empty to avoid exception if
  193. // we would use 'new Guid("")'
  194. return string.IsNullOrEmpty(m_AssetId) ? Guid.Empty : new Guid(m_AssetId);
  195. }
  196. internal set
  197. {
  198. string newAssetIdString = value == Guid.Empty ? string.Empty : value.ToString("N");
  199. string oldAssetIdString = m_AssetId;
  200. // they are the same, do nothing
  201. if (oldAssetIdString == newAssetIdString)
  202. {
  203. return;
  204. }
  205. // new is empty
  206. if (string.IsNullOrEmpty(newAssetIdString))
  207. {
  208. Debug.LogError($"Can not set AssetId to empty guid on NetworkIdentity '{name}', old assetId '{oldAssetIdString}'");
  209. return;
  210. }
  211. // old not empty
  212. if (!string.IsNullOrEmpty(oldAssetIdString))
  213. {
  214. Debug.LogError($"Can not Set AssetId on NetworkIdentity '{name}' because it already had an assetId, current assetId '{oldAssetIdString}', attempted new assetId '{newAssetIdString}'");
  215. return;
  216. }
  217. // old is empty
  218. m_AssetId = newAssetIdString;
  219. // Debug.Log($"Settings AssetId on NetworkIdentity '{name}', new assetId '{newAssetIdString}'");
  220. }
  221. }
  222. [SerializeField, HideInInspector] string m_AssetId;
  223. // Keep track of all sceneIds to detect scene duplicates
  224. static readonly Dictionary<ulong, NetworkIdentity> sceneIds =
  225. new Dictionary<ulong, NetworkIdentity>();
  226. /// <summary>Gets the NetworkIdentity from the sceneIds dictionary with the corresponding id</summary>
  227. public static NetworkIdentity GetSceneIdentity(ulong id) => sceneIds[id];
  228. // used when adding players
  229. internal void SetClientOwner(NetworkConnection conn)
  230. {
  231. // do nothing if it already has an owner
  232. if (connectionToClient != null && conn != connectionToClient)
  233. {
  234. Debug.LogError($"Object {this} netId={netId} already has an owner. Use RemoveClientAuthority() first", this);
  235. return;
  236. }
  237. // otherwise set the owner connection
  238. connectionToClient = (NetworkConnectionToClient)conn;
  239. }
  240. static uint nextNetworkId = 1;
  241. internal static uint GetNextNetworkId() => nextNetworkId++;
  242. /// <summary>Resets nextNetworkId = 1</summary>
  243. public static void ResetNextNetworkId() => nextNetworkId = 1;
  244. /// <summary>The delegate type for the clientAuthorityCallback.</summary>
  245. public delegate void ClientAuthorityCallback(NetworkConnection conn, NetworkIdentity identity, bool authorityState);
  246. /// <summary> A callback that can be populated to be notified when the client-authority state of objects changes.</summary>
  247. public static event ClientAuthorityCallback clientAuthorityCallback;
  248. // hasSpawned should always be false before runtime
  249. [SerializeField, HideInInspector] bool hasSpawned;
  250. public bool SpawnedFromInstantiate { get; private set; }
  251. // NetworkBehaviour components are initialized in Awake once.
  252. // Changing them at runtime would get client & server out of sync.
  253. // BUT internal so tests can add them after creating the NetworkIdentity
  254. internal void InitializeNetworkBehaviours()
  255. {
  256. // Get all NetworkBehaviours
  257. // (never null. GetComponents returns [] if none found)
  258. NetworkBehaviours = GetComponents<NetworkBehaviour>();
  259. if (NetworkBehaviours.Length > byte.MaxValue)
  260. Debug.LogError($"Only {byte.MaxValue} NetworkBehaviour components are allowed for NetworkIdentity: {name} because we send the index as byte.", this);
  261. // initialize each one
  262. for (int i = 0; i < NetworkBehaviours.Length; ++i)
  263. {
  264. NetworkBehaviour component = NetworkBehaviours[i];
  265. component.netIdentity = this;
  266. component.ComponentIndex = i;
  267. }
  268. }
  269. // Awake is only called in Play mode.
  270. // internal so we can call it during unit tests too.
  271. internal void Awake()
  272. {
  273. // initialize NetworkBehaviour components.
  274. // Awake() is called immediately after initialization.
  275. // no one can overwrite it because NetworkIdentity is sealed.
  276. // => doing it here is the fastest and easiest solution.
  277. InitializeNetworkBehaviours();
  278. // initialize visibility component. only call GetComponent once.
  279. #pragma warning disable 618
  280. visibility = GetComponent<NetworkVisibility>();
  281. #pragma warning restore 618
  282. if (hasSpawned)
  283. {
  284. Debug.LogError($"{name} has already spawned. Don't call Instantiate for NetworkIdentities that were in the scene since the beginning (aka scene objects). Otherwise the client won't know which object to use for a SpawnSceneObject message.");
  285. SpawnedFromInstantiate = true;
  286. Destroy(gameObject);
  287. }
  288. hasSpawned = true;
  289. }
  290. void OnValidate()
  291. {
  292. // OnValidate is not called when using Instantiate, so we can use
  293. // it to make sure that hasSpawned is false
  294. hasSpawned = false;
  295. #if UNITY_EDITOR
  296. SetupIDs();
  297. #endif
  298. }
  299. #if UNITY_EDITOR
  300. void AssignAssetID(string path)
  301. {
  302. // only set if not empty. fixes https://github.com/vis2k/Mirror/issues/2765
  303. if (!string.IsNullOrEmpty(path))
  304. m_AssetId = AssetDatabase.AssetPathToGUID(path);
  305. }
  306. void AssignAssetID(GameObject prefab) => AssignAssetID(AssetDatabase.GetAssetPath(prefab));
  307. // persistent sceneId assignment
  308. // (because scene objects have no persistent unique ID in Unity)
  309. //
  310. // original UNET used OnPostProcessScene to assign an index based on
  311. // FindObjectOfType<NetworkIdentity> order.
  312. // -> this didn't work because FindObjectOfType order isn't deterministic.
  313. // -> one workaround is to sort them by sibling paths, but it can still
  314. // get out of sync when we open scene2 in editor and we have
  315. // DontDestroyOnLoad objects that messed with the sibling index.
  316. //
  317. // we absolutely need a persistent id. challenges:
  318. // * it needs to be 0 for prefabs
  319. // => we set it to 0 in SetupIDs() if prefab!
  320. // * it needs to be only assigned in edit time, not at runtime because
  321. // only the objects that were in the scene since beginning should have
  322. // a scene id.
  323. // => Application.isPlaying check solves that
  324. // * it needs to detect duplicated sceneIds after duplicating scene
  325. // objects
  326. // => sceneIds dict takes care of that
  327. // * duplicating the whole scene file shouldn't result in duplicate
  328. // scene objects
  329. // => buildIndex is shifted into sceneId for that.
  330. // => if we have no scenes in build index then it doesn't matter
  331. // because by definition a build can't switch to other scenes
  332. // => if we do have scenes in build index then it will be != -1
  333. // note: the duplicated scene still needs to be opened once for it to
  334. // be set properly
  335. // * scene objects need the correct scene index byte even if the scene's
  336. // build index was changed or a duplicated scene wasn't opened yet.
  337. // => OnPostProcessScene is the only function that gets called for
  338. // each scene before runtime, so this is where we set the scene
  339. // byte.
  340. // * disabled scenes in build settings should result in same scene index
  341. // in editor and in build
  342. // => .gameObject.scene.buildIndex filters out disabled scenes by
  343. // default
  344. // * generated sceneIds absolutely need to set scene dirty and force the
  345. // user to resave.
  346. // => Undo.RecordObject does that perfectly.
  347. // * sceneIds should never be generated temporarily for unopened scenes
  348. // when building, otherwise editor and build get out of sync
  349. // => BuildPipeline.isBuildingPlayer check solves that
  350. void AssignSceneID()
  351. {
  352. // we only ever assign sceneIds at edit time, never at runtime.
  353. // by definition, only the original scene objects should get one.
  354. // -> if we assign at runtime then server and client would generate
  355. // different random numbers!
  356. if (Application.isPlaying)
  357. return;
  358. // no valid sceneId yet, or duplicate?
  359. bool duplicate = sceneIds.TryGetValue(sceneId, out NetworkIdentity existing) && existing != null && existing != this;
  360. if (sceneId == 0 || duplicate)
  361. {
  362. // clear in any case, because it might have been a duplicate
  363. sceneId = 0;
  364. // if a scene was never opened and we are building it, then a
  365. // sceneId would be assigned to build but not saved in editor,
  366. // resulting in them getting out of sync.
  367. // => don't ever assign temporary ids. they always need to be
  368. // permanent
  369. // => throw an exception to cancel the build and let the user
  370. // know how to fix it!
  371. if (BuildPipeline.isBuildingPlayer)
  372. throw new InvalidOperationException($"Scene {gameObject.scene.path} needs to be opened and resaved before building, because the scene object {name} has no valid sceneId yet.");
  373. // if we generate the sceneId then we MUST be sure to set dirty
  374. // in order to save the scene object properly. otherwise it
  375. // would be regenerated every time we reopen the scene, and
  376. // upgrading would be very difficult.
  377. // -> Undo.RecordObject is the new EditorUtility.SetDirty!
  378. // -> we need to call it before changing.
  379. Undo.RecordObject(this, "Generated SceneId");
  380. // generate random sceneId part (0x00000000FFFFFFFF)
  381. uint randomId = Utils.GetTrueRandomUInt();
  382. // only assign if not a duplicate of an existing scene id
  383. // (small chance, but possible)
  384. duplicate = sceneIds.TryGetValue(randomId, out existing) && existing != null && existing != this;
  385. if (!duplicate)
  386. {
  387. sceneId = randomId;
  388. //Debug.Log($"{name} in scene {gameObject.scene.name} sceneId assigned to:{sceneId:X}");
  389. }
  390. }
  391. // add to sceneIds dict no matter what
  392. // -> even if we didn't generate anything new, because we still need
  393. // existing sceneIds in there to check duplicates
  394. sceneIds[sceneId] = this;
  395. }
  396. // copy scene path hash into sceneId for scene objects.
  397. // this is the only way for scene file duplication to not contain
  398. // duplicate sceneIds as it seems.
  399. // -> sceneId before: 0x00000000AABBCCDD
  400. // -> then we clear the left 4 bytes, so that our 'OR' uses 0x00000000
  401. // -> then we OR the hash into the 0x00000000 part
  402. // -> buildIndex is not enough, because Editor and Build have different
  403. // build indices if there are disabled scenes in build settings, and
  404. // if no scene is in build settings then Editor and Build have
  405. // different indices too (Editor=0, Build=-1)
  406. // => ONLY USE THIS FROM POSTPROCESSSCENE!
  407. public void SetSceneIdSceneHashPartInternal()
  408. {
  409. // Use `ToLower` to that because BuildPipeline.BuildPlayer is case insensitive but hash is case sensitive
  410. // If the scene in the project is `forest.unity` but `Forest.unity` is given to BuildPipeline then the
  411. // BuildPipeline will use `Forest.unity` for the build and create a different hash than the editor will.
  412. // Using ToLower will mean the hash will be the same for these 2 paths
  413. // Assets/Scenes/Forest.unity
  414. // Assets/Scenes/forest.unity
  415. string scenePath = gameObject.scene.path.ToLower();
  416. // get deterministic scene hash
  417. uint pathHash = (uint)scenePath.GetStableHashCode();
  418. // shift hash from 0x000000FFFFFFFF to 0xFFFFFFFF00000000
  419. ulong shiftedHash = (ulong)pathHash << 32;
  420. // OR into scene id
  421. sceneId = (sceneId & 0xFFFFFFFF) | shiftedHash;
  422. // log it. this is incredibly useful to debug sceneId issues.
  423. //Debug.Log($"{name} in scene {gameObject.scene.name} scene index hash {pathHash:X} copied into sceneId {sceneId:X}");
  424. }
  425. void SetupIDs()
  426. {
  427. // is this a prefab?
  428. if (Utils.IsPrefab(gameObject))
  429. {
  430. // force 0 for prefabs
  431. sceneId = 0;
  432. AssignAssetID(gameObject);
  433. }
  434. // are we currently in prefab editing mode? aka prefab stage
  435. // => check prefabstage BEFORE SceneObjectWithPrefabParent
  436. // (fixes https://github.com/vis2k/Mirror/issues/976)
  437. // => if we don't check GetCurrentPrefabStage and only check
  438. // GetPrefabStage(gameObject), then the 'else' case where we
  439. // assign a sceneId and clear the assetId would still be
  440. // triggered for prefabs. in other words: if we are in prefab
  441. // stage, do not bother with anything else ever!
  442. else if (PrefabStageUtility.GetCurrentPrefabStage() != null)
  443. {
  444. // when modifying a prefab in prefab stage, Unity calls
  445. // OnValidate for that prefab and for all scene objects based on
  446. // that prefab.
  447. //
  448. // is this GameObject the prefab that we modify, and not just a
  449. // scene object based on the prefab?
  450. // * GetCurrentPrefabStage = 'are we editing ANY prefab?'
  451. // * GetPrefabStage(go) = 'are we editing THIS prefab?'
  452. if (PrefabStageUtility.GetPrefabStage(gameObject) != null)
  453. {
  454. // force 0 for prefabs
  455. sceneId = 0;
  456. //Debug.Log($"{name} scene:{gameObject.scene.name} sceneid reset to 0 because CurrentPrefabStage={PrefabStageUtility.GetCurrentPrefabStage()} PrefabStage={PrefabStageUtility.GetPrefabStage(gameObject)}");
  457. // get path from PrefabStage for this prefab
  458. #if UNITY_2020_1_OR_NEWER
  459. string path = PrefabStageUtility.GetPrefabStage(gameObject).assetPath;
  460. #else
  461. string path = PrefabStageUtility.GetPrefabStage(gameObject).prefabAssetPath;
  462. #endif
  463. AssignAssetID(path);
  464. }
  465. }
  466. // is this a scene object with prefab parent?
  467. else if (Utils.IsSceneObjectWithPrefabParent(gameObject, out GameObject prefab))
  468. {
  469. AssignSceneID();
  470. AssignAssetID(prefab);
  471. }
  472. else
  473. {
  474. AssignSceneID();
  475. // IMPORTANT: DO NOT clear assetId at runtime!
  476. // => fixes a bug where clicking any of the NetworkIdentity
  477. // properties (like ServerOnly/ForceHidden) at runtime would
  478. // call OnValidate
  479. // => OnValidate gets into this else case here because prefab
  480. // connection isn't known at runtime
  481. // => then we would clear the previously assigned assetId
  482. // => and NetworkIdentity couldn't be spawned on other clients
  483. // anymore because assetId was cleared
  484. if (!EditorApplication.isPlaying)
  485. {
  486. m_AssetId = "";
  487. }
  488. // don't log. would show a lot when pressing play in uMMORPG/uSurvival/etc.
  489. //else Debug.Log($"Avoided clearing assetId at runtime for {name} after (probably) clicking any of the NetworkIdentity properties.");
  490. }
  491. }
  492. #endif
  493. // OnDestroy is called for all SPAWNED NetworkIdentities
  494. // => scene objects aren't destroyed. it's not called for them.
  495. //
  496. // Note: Unity will Destroy all networked objects on Scene Change, so we
  497. // have to handle that here silently. That means we cannot have any
  498. // warning or logging in this method.
  499. void OnDestroy()
  500. {
  501. // Objects spawned from Instantiate are not allowed so are destroyed right away
  502. // we don't want to call NetworkServer.Destroy if this is the case
  503. if (SpawnedFromInstantiate)
  504. return;
  505. // If false the object has already been unspawned
  506. // if it is still true, then we need to unspawn it
  507. // if destroy is already called don't call it again
  508. if (isServer && !destroyCalled)
  509. {
  510. // Do not add logging to this (see above)
  511. NetworkServer.Destroy(gameObject);
  512. }
  513. if (isLocalPlayer)
  514. {
  515. // previously there was a bug where isLocalPlayer was
  516. // false in OnDestroy because it was dynamically defined as:
  517. // isLocalPlayer => NetworkClient.localPlayer == this
  518. // we fixed it by setting isLocalPlayer manually and never
  519. // resetting it.
  520. //
  521. // BUT now we need to be aware of a possible data race like in
  522. // our rooms example:
  523. // => GamePlayer is in world
  524. // => player returns to room
  525. // => GamePlayer is destroyed
  526. // => NetworkClient.localPlayer is set to RoomPlayer
  527. // => GamePlayer.OnDestroy is called 1 frame later
  528. // => GamePlayer.OnDestroy 'isLocalPlayer' is true, so here we
  529. // are trying to clear NetworkClient.localPlayer
  530. // => which would overwrite the new RoomPlayer local player
  531. //
  532. // FIXED by simply only clearing if NetworkClient.localPlayer
  533. // still points to US!
  534. // => see also: https://github.com/vis2k/Mirror/issues/2635
  535. if (NetworkClient.localPlayer == this)
  536. NetworkClient.localPlayer = null;
  537. }
  538. }
  539. internal void OnStartServer()
  540. {
  541. // do nothing if already spawned
  542. if (isServer)
  543. return;
  544. // set isServer flag
  545. isServer = true;
  546. // set isLocalPlayer earlier, in case OnStartLocalplayer is called
  547. // AFTER OnStartClient, in which case it would still be falsse here.
  548. // many projects will check isLocalPlayer in OnStartClient though.
  549. // TODO ideally set isLocalPlayer when NetworkClient.localPlayer is set?
  550. if (NetworkClient.localPlayer == this)
  551. {
  552. isLocalPlayer = true;
  553. }
  554. // If the instance/net ID is invalid here then this is an object instantiated from a prefab and the server should assign a valid ID
  555. // NOTE: this might not be necessary because the above m_IsServer
  556. // check already checks netId. BUT this case here checks only
  557. // netId, so it would still check cases where isServer=false
  558. // but netId!=0.
  559. if (netId != 0)
  560. {
  561. // This object has already been spawned, this method might be called again
  562. // if we try to respawn all objects. This can happen when we add a scene
  563. // in that case there is nothing else to do.
  564. return;
  565. }
  566. netId = GetNextNetworkId();
  567. observers = new Dictionary<int, NetworkConnection>();
  568. //Debug.Log($"OnStartServer {this} NetId:{netId} SceneId:{sceneId:X}");
  569. // add to spawned (note: the original EnableIsServer isn't needed
  570. // because we already set m_isServer=true above)
  571. NetworkServer.spawned[netId] = this;
  572. // in host mode we set isClient true before calling OnStartServer,
  573. // otherwise isClient is false in OnStartServer.
  574. if (NetworkClient.active)
  575. {
  576. isClient = true;
  577. }
  578. foreach (NetworkBehaviour comp in NetworkBehaviours)
  579. {
  580. // an exception in OnStartServer should be caught, so that one
  581. // component's exception doesn't stop all other components from
  582. // being initialized
  583. // => this is what Unity does for Start() etc. too.
  584. // one exception doesn't stop all the other Start() calls!
  585. try
  586. {
  587. comp.OnStartServer();
  588. }
  589. catch (Exception e)
  590. {
  591. Debug.LogException(e, comp);
  592. }
  593. }
  594. }
  595. internal void OnStopServer()
  596. {
  597. foreach (NetworkBehaviour comp in NetworkBehaviours)
  598. {
  599. // an exception in OnStartServer should be caught, so that one
  600. // component's exception doesn't stop all other components from
  601. // being initialized
  602. // => this is what Unity does for Start() etc. too.
  603. // one exception doesn't stop all the other Start() calls!
  604. try
  605. {
  606. comp.OnStopServer();
  607. }
  608. catch (Exception e)
  609. {
  610. Debug.LogException(e, comp);
  611. }
  612. }
  613. }
  614. bool clientStarted;
  615. internal void OnStartClient()
  616. {
  617. if (clientStarted)
  618. return;
  619. clientStarted = true;
  620. isClient = true;
  621. // set isLocalPlayer earlier, in case OnStartLocalplayer is called
  622. // AFTER OnStartClient, in which case it would still be falsse here.
  623. // many projects will check isLocalPlayer in OnStartClient though.
  624. // TODO ideally set isLocalPlayer when NetworkClient.localPlayer is set?
  625. if (NetworkClient.localPlayer == this)
  626. {
  627. isLocalPlayer = true;
  628. }
  629. // Debug.Log($"OnStartClient {gameObject} netId:{netId}");
  630. foreach (NetworkBehaviour comp in NetworkBehaviours)
  631. {
  632. // an exception in OnStartClient should be caught, so that one
  633. // component's exception doesn't stop all other components from
  634. // being initialized
  635. // => this is what Unity does for Start() etc. too.
  636. // one exception doesn't stop all the other Start() calls!
  637. try
  638. {
  639. // user implemented startup
  640. comp.OnStartClient();
  641. }
  642. catch (Exception e)
  643. {
  644. Debug.LogException(e, comp);
  645. }
  646. }
  647. }
  648. internal void OnStopClient()
  649. {
  650. foreach (NetworkBehaviour comp in NetworkBehaviours)
  651. {
  652. // an exception in OnStopClient should be caught, so that
  653. // one component's exception doesn't stop all other components
  654. // from being initialized
  655. // => this is what Unity does for Start() etc. too.
  656. // one exception doesn't stop all the other Start() calls!
  657. try
  658. {
  659. comp.OnStopClient();
  660. }
  661. catch (Exception e)
  662. {
  663. Debug.LogException(e, comp);
  664. }
  665. }
  666. }
  667. // TODO any way to make this not static?
  668. // introduced in https://github.com/vis2k/Mirror/commit/c7530894788bb843b0f424e8f25029efce72d8ca#diff-dc8b7a5a67840f75ccc884c91b9eb76ab7311c9ca4360885a7e41d980865bdc2
  669. // for PR https://github.com/vis2k/Mirror/pull/1263
  670. //
  671. // explanation:
  672. // we send the spawn message multiple times. Whenever an object changes
  673. // authority, we send the spawn message again for the object. This is
  674. // necessary because we need to reinitialize all variables when
  675. // ownership change due to sync to owner feature.
  676. // Without this static, the second time we get the spawn message we
  677. // would call OnStartLocalPlayer again on the same object
  678. static NetworkIdentity previousLocalPlayer = null;
  679. internal void OnStartLocalPlayer()
  680. {
  681. if (previousLocalPlayer == this)
  682. return;
  683. previousLocalPlayer = this;
  684. isLocalPlayer = true;
  685. foreach (NetworkBehaviour comp in NetworkBehaviours)
  686. {
  687. // an exception in OnStartLocalPlayer should be caught, so that
  688. // one component's exception doesn't stop all other components
  689. // from being initialized
  690. // => this is what Unity does for Start() etc. too.
  691. // one exception doesn't stop all the other Start() calls!
  692. try
  693. {
  694. comp.OnStartLocalPlayer();
  695. }
  696. catch (Exception e)
  697. {
  698. Debug.LogException(e, comp);
  699. }
  700. }
  701. }
  702. bool hadAuthority;
  703. internal void NotifyAuthority()
  704. {
  705. if (!hadAuthority && hasAuthority)
  706. OnStartAuthority();
  707. if (hadAuthority && !hasAuthority)
  708. OnStopAuthority();
  709. hadAuthority = hasAuthority;
  710. }
  711. internal void OnStartAuthority()
  712. {
  713. foreach (NetworkBehaviour comp in NetworkBehaviours)
  714. {
  715. // an exception in OnStartAuthority should be caught, so that one
  716. // component's exception doesn't stop all other components from
  717. // being initialized
  718. // => this is what Unity does for Start() etc. too.
  719. // one exception doesn't stop all the other Start() calls!
  720. try
  721. {
  722. comp.OnStartAuthority();
  723. }
  724. catch (Exception e)
  725. {
  726. Debug.LogException(e, comp);
  727. }
  728. }
  729. }
  730. internal void OnStopAuthority()
  731. {
  732. foreach (NetworkBehaviour comp in NetworkBehaviours)
  733. {
  734. // an exception in OnStopAuthority should be caught, so that one
  735. // component's exception doesn't stop all other components from
  736. // being initialized
  737. // => this is what Unity does for Start() etc. too.
  738. // one exception doesn't stop all the other Start() calls!
  739. try
  740. {
  741. comp.OnStopAuthority();
  742. }
  743. catch (Exception e)
  744. {
  745. Debug.LogException(e, comp);
  746. }
  747. }
  748. }
  749. // interest management /////////////////////////////////////////////////
  750. // obsoletes to still support ProximityChecker while transitioning to
  751. // global Interest Management
  752. // Deprecated 2021-02-17
  753. [Obsolete("Use NetworkServer.RebuildObservers(identity, initialize) instead.")]
  754. public void RebuildObservers(bool initialize) => NetworkServer.RebuildObservers(this, initialize);
  755. // vis2k: readstring bug prevention: https://github.com/vis2k/Mirror/issues/2617
  756. // -> OnSerialize writes length,componentData,length,componentData,...
  757. // -> OnDeserialize carefully extracts each data, then deserializes each component with separate readers
  758. // -> it will be impossible to read too many or too few bytes in OnDeserialize
  759. // -> we can properly track down errors
  760. bool OnSerializeSafely(NetworkBehaviour comp, NetworkWriter writer, bool initialState)
  761. {
  762. // write placeholder length bytes
  763. // (jumping back later is WAY faster than allocating a temporary
  764. // writer for the payload, then writing payload.size, payload)
  765. int headerPosition = writer.Position;
  766. // no varint because we don't know the final size yet
  767. writer.WriteInt(0);
  768. int contentPosition = writer.Position;
  769. // write payload
  770. bool result = false;
  771. try
  772. {
  773. result = comp.OnSerialize(writer, initialState);
  774. }
  775. catch (Exception e)
  776. {
  777. // show a detailed error and let the user know what went wrong
  778. Debug.LogError($"OnSerialize failed for: object={name} component={comp.GetType()} sceneId={sceneId:X}\n\n{e}");
  779. }
  780. int endPosition = writer.Position;
  781. // fill in length now
  782. writer.Position = headerPosition;
  783. writer.WriteInt(endPosition - contentPosition);
  784. writer.Position = endPosition;
  785. //Debug.Log($"OnSerializeSafely written for object {comp.name} component:{comp.GetType()} sceneId:{sceneId:X} header:{headerPosition} content:{contentPosition} end:{endPosition} contentSize:{endPosition - contentPosition}");
  786. return result;
  787. }
  788. // serialize all components using dirtyComponentsMask
  789. // check ownerWritten/observersWritten to know if anything was written
  790. // We pass dirtyComponentsMask into this function so that we can check
  791. // if any Components are dirty before creating writers
  792. internal void OnSerializeAllSafely(bool initialState, NetworkWriter ownerWriter, NetworkWriter observersWriter)
  793. {
  794. // check if components are in byte.MaxRange just to be 100% sure
  795. // that we avoid overflows
  796. NetworkBehaviour[] components = NetworkBehaviours;
  797. if (components.Length > byte.MaxValue)
  798. throw new IndexOutOfRangeException($"{name} has more than {byte.MaxValue} components. This is not supported.");
  799. // serialize all components
  800. for (int i = 0; i < components.Length; ++i)
  801. {
  802. // is this component dirty?
  803. // -> always serialize if initialState so all components are included in spawn packet
  804. // -> note: IsDirty() is false if the component isn't dirty or sendInterval isn't elapsed yet
  805. NetworkBehaviour comp = components[i];
  806. if (initialState || comp.IsDirty())
  807. {
  808. //Debug.Log($"OnSerializeAllSafely: {name} -> {comp.GetType()} initial:{ initialState}");
  809. // remember start position in case we need to copy it into
  810. // observers writer too
  811. int startPosition = ownerWriter.Position;
  812. // write index as byte [0..255]
  813. ownerWriter.WriteByte((byte)i);
  814. // serialize into ownerWriter first
  815. // (owner always gets everything!)
  816. OnSerializeSafely(comp, ownerWriter, initialState);
  817. // copy into observersWriter too if SyncMode.Observers
  818. // -> we copy instead of calling OnSerialize again because
  819. // we don't know what magic the user does in OnSerialize.
  820. // -> it's not guaranteed that calling it twice gets the
  821. // same result
  822. // -> it's not guaranteed that calling it twice doesn't mess
  823. // with the user's OnSerialize timing code etc.
  824. // => so we just copy the result without touching
  825. // OnSerialize again
  826. if (comp.syncMode == SyncMode.Observers)
  827. {
  828. ArraySegment<byte> segment = ownerWriter.ToArraySegment();
  829. int length = ownerWriter.Position - startPosition;
  830. observersWriter.WriteBytes(segment.Array, startPosition, length);
  831. }
  832. }
  833. }
  834. }
  835. // get cached serialization for this tick (or serialize if none yet)
  836. // IMPORTANT: int tick avoids floating point inaccuracy over days/weeks
  837. internal NetworkIdentitySerialization GetSerializationAtTick(int tick)
  838. {
  839. // reserialize if tick is different than last changed.
  840. // NOTE: != instead of < because int.max+1 overflows at some point.
  841. if (lastSerialization.tick != tick)
  842. {
  843. // reset
  844. lastSerialization.ownerWriter.Position = 0;
  845. lastSerialization.observersWriter.Position = 0;
  846. // serialize
  847. OnSerializeAllSafely(false,
  848. lastSerialization.ownerWriter,
  849. lastSerialization.observersWriter);
  850. // set tick
  851. lastSerialization.tick = tick;
  852. //Debug.Log($"{name} (netId={netId}) serialized for tick={tickTimeStamp}");
  853. }
  854. // return it
  855. return lastSerialization;
  856. }
  857. void OnDeserializeSafely(NetworkBehaviour comp, NetworkReader reader, bool initialState)
  858. {
  859. // read header as 4 bytes and calculate this chunk's start+end
  860. int contentSize = reader.ReadInt();
  861. int chunkStart = reader.Position;
  862. int chunkEnd = reader.Position + contentSize;
  863. // call OnDeserialize and wrap it in a try-catch block so there's no
  864. // way to mess up another component's deserialization
  865. try
  866. {
  867. //Debug.Log($"OnDeserializeSafely: {comp.name} component:{comp.GetType()} sceneId:{sceneId:X} length:{contentSize}");
  868. comp.OnDeserialize(reader, initialState);
  869. }
  870. catch (Exception e)
  871. {
  872. // show a detailed error and let the user know what went wrong
  873. Debug.LogError($"OnDeserialize failed Exception={e.GetType()} (see below) object={name} component={comp.GetType()} sceneId={sceneId:X} length={contentSize}. Possible Reasons:\n" +
  874. $" * Do {comp.GetType()}'s OnSerialize and OnDeserialize calls write the same amount of data({contentSize} bytes)? \n" +
  875. $" * Was there an exception in {comp.GetType()}'s OnSerialize/OnDeserialize code?\n" +
  876. $" * Are the server and client the exact same project?\n" +
  877. $" * Maybe this OnDeserialize call was meant for another GameObject? The sceneIds can easily get out of sync if the Hierarchy was modified only in the client OR the server. Try rebuilding both.\n\n" +
  878. $"Exception {e}");
  879. }
  880. // now the reader should be EXACTLY at 'before + size'.
  881. // otherwise the component read too much / too less data.
  882. if (reader.Position != chunkEnd)
  883. {
  884. // warn the user
  885. int bytesRead = reader.Position - chunkStart;
  886. Debug.LogWarning($"OnDeserialize was expected to read {contentSize} instead of {bytesRead} bytes for object:{name} component={comp.GetType()} sceneId={sceneId:X}. Make sure that OnSerialize and OnDeserialize write/read the same amount of data in all cases.");
  887. // fix the position, so the following components don't all fail
  888. reader.Position = chunkEnd;
  889. }
  890. }
  891. internal void OnDeserializeAllSafely(NetworkReader reader, bool initialState)
  892. {
  893. if (NetworkBehaviours == null)
  894. {
  895. Debug.LogError($"NetworkBehaviours array is null on {gameObject.name}!\n" +
  896. $"Typically this can happen when a networked object is a child of a " +
  897. $"non-networked parent that's disabled, preventing Awake on the networked object " +
  898. $"from being invoked, where the NetworkBehaviours array is initialized.", gameObject);
  899. return;
  900. }
  901. // deserialize all components that were received
  902. NetworkBehaviour[] components = NetworkBehaviours;
  903. while (reader.Remaining > 0)
  904. {
  905. // read & check index [0..255]
  906. byte index = reader.ReadByte();
  907. if (index < components.Length)
  908. {
  909. // deserialize this component
  910. OnDeserializeSafely(components[index], reader, initialState);
  911. }
  912. }
  913. }
  914. // Helper function to handle Command/Rpc
  915. internal void HandleRemoteCall(int componentIndex, int functionHash, MirrorInvokeType invokeType, NetworkReader reader, NetworkConnectionToClient senderConnection = null)
  916. {
  917. // check if unity object has been destroyed
  918. if (this == null)
  919. {
  920. Debug.LogWarning($"{invokeType} [{functionHash}] received for deleted object [netId={netId}]");
  921. return;
  922. }
  923. // find the right component to invoke the function on
  924. if (componentIndex < 0 || componentIndex >= NetworkBehaviours.Length)
  925. {
  926. Debug.LogWarning($"Component [{componentIndex}] not found for [netId={netId}]");
  927. return;
  928. }
  929. NetworkBehaviour invokeComponent = NetworkBehaviours[componentIndex];
  930. if (!RemoteCallHelper.InvokeHandlerDelegate(functionHash, invokeType, reader, invokeComponent, senderConnection))
  931. {
  932. Debug.LogError($"Found no receiver for incoming {invokeType} [{functionHash}] on {gameObject.name}, the server and client should have the same NetworkBehaviour instances [netId={netId}].");
  933. }
  934. }
  935. // Runs on server
  936. internal CommandInfo GetCommandInfo(int componentIndex, int cmdHash)
  937. {
  938. // check if unity object has been destroyed
  939. if (this == null)
  940. {
  941. // error can be logged later
  942. return default;
  943. }
  944. // find the right component to invoke the function on
  945. if (0 <= componentIndex && componentIndex < NetworkBehaviours.Length)
  946. {
  947. NetworkBehaviour invokeComponent = NetworkBehaviours[componentIndex];
  948. return RemoteCallHelper.GetCommandInfo(cmdHash, invokeComponent);
  949. }
  950. else
  951. {
  952. // error can be logged later
  953. return default;
  954. }
  955. }
  956. internal void AddObserver(NetworkConnection conn)
  957. {
  958. if (observers == null)
  959. {
  960. Debug.LogError($"AddObserver for {gameObject} observer list is null");
  961. return;
  962. }
  963. if (observers.ContainsKey(conn.connectionId))
  964. {
  965. // if we try to add a connectionId that was already added, then
  966. // we may have generated one that was already in use.
  967. return;
  968. }
  969. // Debug.Log($"Added observer: {conn.address} added for {gameObject}");
  970. // if we previously had no observers, then clear all dirty bits once.
  971. // a monster's health may have changed while it had no observers.
  972. // but that change (= the dirty bits) don't matter as soon as the
  973. // first observer comes.
  974. // -> first observer gets full spawn packet
  975. // -> afterwards it gets delta packet
  976. // => if we don't clear previous dirty bits, observer would get
  977. // the health change because the bit was still set.
  978. // => ultimately this happens because spawn doesn't reset dirty
  979. // bits
  980. // => which happens because spawn happens separately, instead of
  981. // in Broadcast() (which will be changed in the future)
  982. //
  983. // NOTE that NetworkServer.Broadcast previously cleared dirty bits
  984. // for ALL SPAWNED that don't have observers. that was super
  985. // expensive. doing it when adding the first observer has the
  986. // same result, without the O(N) iteration in Broadcast().
  987. //
  988. // TODO remove this after moving spawning into Broadcast()!
  989. if (observers.Count == 0)
  990. {
  991. ClearAllComponentsDirtyBits();
  992. }
  993. observers[conn.connectionId] = conn;
  994. conn.AddToObserving(this);
  995. }
  996. // this is used when a connection is destroyed, since the "observers" property is read-only
  997. internal void RemoveObserver(NetworkConnection conn)
  998. {
  999. observers?.Remove(conn.connectionId);
  1000. }
  1001. // Called when NetworkIdentity is destroyed
  1002. internal void ClearObservers()
  1003. {
  1004. if (observers != null)
  1005. {
  1006. foreach (NetworkConnection conn in observers.Values)
  1007. {
  1008. conn.RemoveFromObserving(this, true);
  1009. }
  1010. observers.Clear();
  1011. }
  1012. }
  1013. /// <summary>Assign control of an object to a client via the client's NetworkConnection.</summary>
  1014. // This causes hasAuthority to be set on the client that owns the object,
  1015. // and NetworkBehaviour.OnStartAuthority will be called on that client.
  1016. // This object then will be in the NetworkConnection.clientOwnedObjects
  1017. // list for the connection.
  1018. //
  1019. // Authority can be removed with RemoveClientAuthority. Only one client
  1020. // can own an object at any time. This does not need to be called for
  1021. // player objects, as their authority is setup automatically.
  1022. public bool AssignClientAuthority(NetworkConnection conn)
  1023. {
  1024. if (!isServer)
  1025. {
  1026. Debug.LogError("AssignClientAuthority can only be called on the server for spawned objects.");
  1027. return false;
  1028. }
  1029. if (conn == null)
  1030. {
  1031. Debug.LogError($"AssignClientAuthority for {gameObject} owner cannot be null. Use RemoveClientAuthority() instead.");
  1032. return false;
  1033. }
  1034. if (connectionToClient != null && conn != connectionToClient)
  1035. {
  1036. Debug.LogError($"AssignClientAuthority for {gameObject} already has an owner. Use RemoveClientAuthority() first.");
  1037. return false;
  1038. }
  1039. SetClientOwner(conn);
  1040. // The client will match to the existing object
  1041. NetworkServer.SendChangeOwnerMessage(this, conn);
  1042. clientAuthorityCallback?.Invoke(conn, this, true);
  1043. return true;
  1044. }
  1045. /// <summary>Removes ownership for an object.</summary>
  1046. // Applies to objects that had authority set by AssignClientAuthority,
  1047. // or NetworkServer.Spawn with a NetworkConnection parameter included.
  1048. // Authority cannot be removed for player objects.
  1049. public void RemoveClientAuthority()
  1050. {
  1051. if (!isServer)
  1052. {
  1053. Debug.LogError("RemoveClientAuthority can only be called on the server for spawned objects.");
  1054. return;
  1055. }
  1056. if (connectionToClient?.identity == this)
  1057. {
  1058. Debug.LogError("RemoveClientAuthority cannot remove authority for a player object");
  1059. return;
  1060. }
  1061. if (connectionToClient != null)
  1062. {
  1063. clientAuthorityCallback?.Invoke(connectionToClient, this, false);
  1064. NetworkConnectionToClient previousOwner = connectionToClient;
  1065. connectionToClient = null;
  1066. NetworkServer.SendChangeOwnerMessage(this, previousOwner);
  1067. }
  1068. }
  1069. // Reset is called when the user hits the Reset button in the
  1070. // Inspector's context menu or when adding the component the first time.
  1071. // This function is only called in editor mode.
  1072. //
  1073. // Reset() seems to be called only for Scene objects.
  1074. // we can't destroy them (they are always in the scene).
  1075. // instead we disable them and call Reset().
  1076. //
  1077. // OLD COMMENT:
  1078. // Marks the identity for future reset, this is because we cant reset
  1079. // the identity during destroy as people might want to be able to read
  1080. // the members inside OnDestroy(), and we have no way of invoking reset
  1081. // after OnDestroy is called.
  1082. internal void Reset()
  1083. {
  1084. // make sure to call this before networkBehavioursCache is cleared below
  1085. ResetSyncObjects();
  1086. hasSpawned = false;
  1087. clientStarted = false;
  1088. isClient = false;
  1089. isServer = false;
  1090. //isLocalPlayer = false; <- cleared AFTER ClearLocalPlayer below!
  1091. // remove authority flag. This object may be unspawned, not destroyed, on client.
  1092. hasAuthority = false;
  1093. NotifyAuthority();
  1094. netId = 0;
  1095. connectionToServer = null;
  1096. connectionToClient = null;
  1097. ClearObservers();
  1098. // clear local player if it was the local player,
  1099. // THEN reset isLocalPlayer AFTERWARDS
  1100. if (isLocalPlayer)
  1101. {
  1102. // only clear NetworkClient.localPlayer IF IT POINTS TO US!
  1103. // see OnDestroy() comments. it does the same.
  1104. // (https://github.com/vis2k/Mirror/issues/2635)
  1105. if (NetworkClient.localPlayer == this)
  1106. NetworkClient.localPlayer = null;
  1107. }
  1108. isLocalPlayer = false;
  1109. }
  1110. // clear all component's dirty bits no matter what
  1111. internal void ClearAllComponentsDirtyBits()
  1112. {
  1113. foreach (NetworkBehaviour comp in NetworkBehaviours)
  1114. {
  1115. comp.ClearAllDirtyBits();
  1116. }
  1117. }
  1118. // Clear only dirty component's dirty bits. ignores components which
  1119. // may be dirty but not ready to be synced yet (because of syncInterval)
  1120. internal void ClearDirtyComponentsDirtyBits()
  1121. {
  1122. foreach (NetworkBehaviour comp in NetworkBehaviours)
  1123. {
  1124. if (comp.IsDirty())
  1125. {
  1126. comp.ClearAllDirtyBits();
  1127. }
  1128. }
  1129. }
  1130. void ResetSyncObjects()
  1131. {
  1132. // ResetSyncObjects is called by Reset, which is called by Unity.
  1133. // AddComponent() calls Reset().
  1134. // AddComponent() is called before Awake().
  1135. // so NetworkBehaviours may not be initialized yet.
  1136. if (NetworkBehaviours == null)
  1137. return;
  1138. foreach (NetworkBehaviour comp in NetworkBehaviours)
  1139. {
  1140. comp.ResetSyncObjects();
  1141. }
  1142. }
  1143. }
  1144. }