NetworkIdentity.cs 62 KB

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