NetworkBehaviour.cs 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Runtime.CompilerServices;
  5. using UnityEngine;
  6. namespace Mirror
  7. {
  8. // SyncMode decides if a component is synced to all observers, or only owner
  9. public enum SyncMode { Observers, Owner }
  10. // SyncDirection decides if a component is synced from:
  11. // * server to all clients
  12. // * owner client, to server, to all other clients
  13. //
  14. // naming: 'ClientToServer' etc. instead of 'ClientAuthority', because
  15. // that wouldn't be accurate. server's OnDeserialize can still validate
  16. // client data before applying. it's really about direction, not authority.
  17. public enum SyncDirection { ServerToClient, ClientToServer }
  18. /// <summary>Base class for networked components.</summary>
  19. // [RequireComponent(typeof(NetworkIdentity))] disabled to allow child NetworkBehaviours
  20. [AddComponentMenu("")]
  21. [HelpURL("https://mirror-networking.gitbook.io/docs/guides/networkbehaviour")]
  22. public abstract class NetworkBehaviour : MonoBehaviour
  23. {
  24. /// <summary>Sync direction for OnSerialize. ServerToClient by default. ClientToServer for client authority.</summary>
  25. [Tooltip("Server Authority calls OnSerialize on the server and syncs it to clients.\n\nClient Authority calls OnSerialize on the owning client, syncs it to server, which then broadcasts it to all other clients.\n\nUse server authority for cheat safety.")]
  26. [HideInInspector] public SyncDirection syncDirection = SyncDirection.ServerToClient;
  27. /// <summary>sync mode for OnSerialize</summary>
  28. // hidden because NetworkBehaviourInspector shows it only if has OnSerialize.
  29. [Tooltip("By default synced data is sent from the server to all Observers of the object.\nChange this to Owner to only have the server update the client that has ownership authority for this object")]
  30. [HideInInspector] public SyncMode syncMode = SyncMode.Observers;
  31. /// <summary>sync interval for OnSerialize (in seconds)</summary>
  32. // hidden because NetworkBehaviourInspector shows it only if has OnSerialize.
  33. // [0,2] should be enough. anything >2s is too laggy anyway.
  34. //
  35. // NetworkServer & NetworkClient broadcast() are behind a sendInterval timer now.
  36. // it makes sense to keep every component's syncInterval setting at '0' by default.
  37. // otherwise, the overlapping timers could introduce unexpected latency.
  38. // careful: default of '0.1' may
  39. [Tooltip("Time in seconds until next change is synchronized to the client. '0' means send immediately if changed. '0.5' means only send changes every 500ms.\n(This is for state synchronization like SyncVars, SyncLists, OnSerialize. Not for Cmds, Rpcs, etc.)")]
  40. [Range(0, 2)]
  41. [HideInInspector] public float syncInterval = 0;
  42. internal double lastSyncTime;
  43. /// <summary>True if this object is on the server and has been spawned.</summary>
  44. // This is different from NetworkServer.active, which is true if the
  45. // server itself is active rather than this object being active.
  46. public bool isServer => netIdentity.isServer;
  47. /// <summary>True if this object is on the client and has been spawned by the server.</summary>
  48. public bool isClient => netIdentity.isClient;
  49. /// <summary>True if this object is the the client's own local player.</summary>
  50. public bool isLocalPlayer => netIdentity.isLocalPlayer;
  51. /// <summary>True if this object is on the server-only, not host.</summary>
  52. public bool isServerOnly => netIdentity.isServerOnly;
  53. /// <summary>True if this object is on the client-only, not host.</summary>
  54. public bool isClientOnly => netIdentity.isClientOnly;
  55. /// <summary>isOwned is true on the client if this NetworkIdentity is one of the .owned entities of our connection on the server.</summary>
  56. // for example: main player & pets are owned. monsters & npcs aren't.
  57. public bool isOwned => netIdentity.isOwned;
  58. /// <summary>authority is true if we are allowed to modify this component's state. On server, it's true if SyncDirection is ServerToClient. On client, it's true if SyncDirection is ClientToServer and(!) if this object is owned by the client.</summary>
  59. // on the client: if Client->Server SyncDirection and owned
  60. // on the server: if Server->Client SyncDirection
  61. // on the host: if Server->Client SyncDirection (= server owns it), or if Client->Server and owned (=host client owns it)
  62. // in host mode: always true because either server or client always has authority, and host is both.
  63. //
  64. // for example, NetworkTransform:
  65. // client may modify position if ClientAuthority mode and owned
  66. // server may modify position only if server authority
  67. //
  68. // note that in original Mirror, hasAuthority only meant 'isOwned'.
  69. // there was no syncDirection to check.
  70. //
  71. // also note that this is a per-NetworkBehaviour flag.
  72. // another component may not be client authoritative, etc.
  73. public bool authority
  74. {
  75. get
  76. {
  77. // host mode needs to be checked explicitly
  78. if (isClient && isServer) return syncDirection == SyncDirection.ServerToClient || isOwned;
  79. // client-only
  80. if (isClient) return syncDirection == SyncDirection.ClientToServer && isOwned;
  81. // server-only
  82. return syncDirection == SyncDirection.ServerToClient;
  83. }
  84. }
  85. /// <summary>The unique network Id of this object (unique at runtime).</summary>
  86. public uint netId => netIdentity.netId;
  87. /// <summary>Client's network connection to the server. This is only valid for player objects on the client.</summary>
  88. // TODO change to NetworkConnectionToServer, but might cause some breaking
  89. public NetworkConnection connectionToServer => netIdentity.connectionToServer;
  90. /// <summary>Server's network connection to the client. This is only valid for player objects on the server.</summary>
  91. public NetworkConnectionToClient connectionToClient => netIdentity.connectionToClient;
  92. // SyncLists, SyncSets, etc.
  93. protected readonly List<SyncObject> syncObjects = new List<SyncObject>();
  94. // NetworkBehaviourInspector needs to know if we have SyncObjects
  95. internal bool HasSyncObjects() => syncObjects.Count > 0;
  96. // NetworkIdentity based values set from NetworkIdentity.Awake(),
  97. // which is way more simple and way faster than trying to figure out
  98. // component index from in here by searching all NetworkComponents.
  99. /// <summary>Returns the NetworkIdentity of this object</summary>
  100. public NetworkIdentity netIdentity { get; internal set; }
  101. /// <summary>Returns the index of the component on this object</summary>
  102. public byte ComponentIndex { get; internal set; }
  103. // to avoid fully serializing entities every time, we have two options:
  104. // * run a delta compression algorithm
  105. // -> for fixed size types this is as easy as varint(b-a) for all
  106. // -> for dynamically sized types like strings this is not easy.
  107. // algorithms need to detect inserts/deletions, i.e. Myers Diff.
  108. // those are very cpu intensive and barely fast enough for large
  109. // scale multiplayer games (in Unity)
  110. // * or we use dirty bits as meta data about which fields have changed
  111. // -> spares us from running delta algorithms
  112. // -> still supports dynamically sized types
  113. //
  114. // 64 bit mask, tracking up to 64 SyncVars.
  115. protected ulong syncVarDirtyBits { get; private set; }
  116. // 64 bit mask, tracking up to 64 sync collections (internal for tests).
  117. // internal for tests, field for faster access (instead of property)
  118. // TODO 64 SyncLists are too much. consider smaller mask later.
  119. internal ulong syncObjectDirtyBits;
  120. // Weaver replaces '[SyncVar] int health' with 'Networkhealth' property.
  121. // setter calls the hook if value changed.
  122. // if we then modify the [SyncVar] from inside the setter,
  123. // the setter would call the hook and we deadlock.
  124. // hook guard prevents that.
  125. ulong syncVarHookGuard;
  126. // USED BY WEAVER to set syncvars in host mode without deadlocking
  127. protected bool GetSyncVarHookGuard(ulong dirtyBit) =>
  128. (syncVarHookGuard & dirtyBit) != 0UL;
  129. // USED BY WEAVER to set syncvars in host mode without deadlocking
  130. protected void SetSyncVarHookGuard(ulong dirtyBit, bool value)
  131. {
  132. // set the bit
  133. if (value)
  134. syncVarHookGuard |= dirtyBit;
  135. // clear the bit
  136. else
  137. syncVarHookGuard &= ~dirtyBit;
  138. }
  139. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  140. void SetSyncObjectDirtyBit(ulong dirtyBit)
  141. {
  142. syncObjectDirtyBits |= dirtyBit;
  143. }
  144. /// <summary>Set as dirty so that it's synced to clients again.</summary>
  145. // these are masks, not bit numbers, ie. 110011b not '2' for 2nd bit.
  146. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  147. public void SetSyncVarDirtyBit(ulong dirtyBit)
  148. {
  149. syncVarDirtyBits |= dirtyBit;
  150. }
  151. /// <summary>Set as dirty to trigger OnSerialize & send. Dirty bits are cleared after the send.</summary>
  152. // previously one had to use SetSyncVarDirtyBit(1), which is confusing.
  153. // simply reuse SetSyncVarDirtyBit for now.
  154. // instead of adding another field.
  155. // syncVarDirtyBits does trigger OnSerialize as well.
  156. //
  157. // it's important to set _all_ bits as dirty.
  158. // for example, server needs to broadcast ClientToServer components.
  159. // if we only set the first bit, only that SyncVar would be broadcast.
  160. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  161. public void SetDirty() => SetSyncVarDirtyBit(ulong.MaxValue);
  162. // true if syncInterval elapsed and any SyncVar or SyncObject is dirty
  163. // OR both bitmasks. != 0 if either was dirty.
  164. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  165. public bool IsDirty() =>
  166. // check bits first. this is basically free.
  167. (syncVarDirtyBits | syncObjectDirtyBits) != 0UL &&
  168. // only check time if bits were dirty. this is more expensive.
  169. NetworkTime.localTime - lastSyncTime >= syncInterval;
  170. /// <summary>Clears all the dirty bits that were set by SetSyncVarDirtyBit() (formally SetDirtyBits)</summary>
  171. // automatically invoked when an update is sent for this object, but can
  172. // be called manually as well.
  173. public void ClearAllDirtyBits()
  174. {
  175. lastSyncTime = NetworkTime.localTime;
  176. syncVarDirtyBits = 0L;
  177. syncObjectDirtyBits = 0L;
  178. // clear all unsynchronized changes in syncobjects
  179. // (Linq allocates, use for instead)
  180. for (int i = 0; i < syncObjects.Count; ++i)
  181. {
  182. syncObjects[i].ClearChanges();
  183. }
  184. }
  185. // this gets called in the constructor by the weaver
  186. // for every SyncObject in the component (e.g. SyncLists).
  187. // We collect all of them and we synchronize them with OnSerialize/OnDeserialize
  188. protected void InitSyncObject(SyncObject syncObject)
  189. {
  190. if (syncObject == null)
  191. {
  192. Debug.LogError("Uninitialized SyncObject. Manually call the constructor on your SyncList, SyncSet, SyncDictionary or SyncField<T>");
  193. return;
  194. }
  195. // add it, remember the index in list (if Count=0, index=0 etc.)
  196. int index = syncObjects.Count;
  197. syncObjects.Add(syncObject);
  198. // OnDirty needs to set nth bit in our dirty mask
  199. ulong nthBit = 1UL << index;
  200. syncObject.OnDirty = () => SetSyncObjectDirtyBit(nthBit);
  201. // who is allowed to modify SyncList/SyncSet/etc.:
  202. // on client: only if owned ClientToserver
  203. // on server: only if ServerToClient.
  204. // but also for initial state when spawning.
  205. // need to set a lambda because 'isClient' isn't available in
  206. // InitSyncObject yet, which is called from the constructor.
  207. syncObject.IsWritable = () =>
  208. {
  209. // carefully check each mode separately to ensure correct results.
  210. // fixes: https://github.com/MirrorNetworking/Mirror/issues/3342
  211. // normally we would check isServer / isClient here.
  212. // users may add to SyncLists before the object was spawned.
  213. // isServer / isClient would still be false.
  214. // so we need to check NetworkServer/Client.active here instead.
  215. // host mode: any ServerToClient and any local client owned
  216. if (NetworkServer.active && NetworkClient.active)
  217. return syncDirection == SyncDirection.ServerToClient || isOwned;
  218. // server only: any ServerToClient
  219. if (NetworkServer.active)
  220. return syncDirection == SyncDirection.ServerToClient;
  221. // client only: only ClientToServer and owned
  222. if (NetworkClient.active)
  223. {
  224. // spawned: only ClientToServer and owned
  225. if (netId != 0) return syncDirection == SyncDirection.ClientToServer && isOwned;
  226. // not spawned (character selection previews, etc.): always allow
  227. // fixes https://github.com/MirrorNetworking/Mirror/issues/3343
  228. return true;
  229. }
  230. // undefined behaviour should throw to make it very obvious
  231. throw new Exception("InitSyncObject: IsWritable: neither NetworkServer nor NetworkClient are active.");
  232. };
  233. // when do we record changes:
  234. // on client: only if owned ClientToServer
  235. // on server: only if we have observers.
  236. // prevents ever growing .changes lists:
  237. // if a monster has no observers but we keep modifing a SyncObject,
  238. // then the changes would never be flushed and keep growing,
  239. // because OnSerialize isn't called without observers.
  240. syncObject.IsRecording = () =>
  241. {
  242. // carefully check each mode separately to ensure correct results.
  243. // fixes: https://github.com/MirrorNetworking/Mirror/issues/3342
  244. // host mode: only if observed
  245. if (isServer && isClient) return netIdentity.observers.Count > 0;
  246. // server only: only if observed
  247. if (isServer) return netIdentity.observers.Count > 0;
  248. // client only: only ClientToServer and owned
  249. if (isClient) return syncDirection == SyncDirection.ClientToServer && isOwned;
  250. // users may add to SyncLists before the object was spawned.
  251. // isServer / isClient would still be false.
  252. // in that case, allow modifying but don't record changes yet.
  253. return false;
  254. };
  255. }
  256. protected virtual void OnValidate()
  257. {
  258. // we now allow child NetworkBehaviours.
  259. // we can not [RequireComponent(typeof(NetworkIdentity))] anymore.
  260. // instead, we need to ensure a NetworkIdentity is somewhere in the
  261. // parents.
  262. // only run this in Editor. don't add more runtime overhead.
  263. // GetComponentInParent(includeInactive) is needed because Prefabs are not
  264. // considered active, so this check requires to scan inactive.
  265. #if UNITY_EDITOR
  266. #if UNITY_2021_3_OR_NEWER // 2021 has GetComponentInParents(active)
  267. if (GetComponent<NetworkIdentity>() == null &&
  268. GetComponentInParent<NetworkIdentity>(true) == null)
  269. {
  270. Debug.LogError($"{GetType()} on {name} requires a NetworkIdentity. Please add a NetworkIdentity component to {name} or it's parents.");
  271. }
  272. #elif UNITY_2020_3_OR_NEWER // 2020 only has GetComponentsInParents(active), we can use this too
  273. NetworkIdentity[] parentsIds = GetComponentsInParent<NetworkIdentity>(true);
  274. int parentIdsCount = parentsIds != null ? parentsIds.Length : 0;
  275. if (GetComponent<NetworkIdentity>() == null && parentIdsCount == 0)
  276. {
  277. Debug.LogError($"{GetType()} on {name} requires a NetworkIdentity. Please add a NetworkIdentity component to {name} or it's parents.");
  278. }
  279. #endif
  280. #endif
  281. }
  282. // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
  283. protected void SendCommandInternal(string functionFullName, int functionHashCode, NetworkWriter writer, int channelId, bool requiresAuthority = true)
  284. {
  285. // this was in Weaver before
  286. // NOTE: we could remove this later to allow calling Cmds on Server
  287. // to avoid Wrapper functions. a lot of people requested this.
  288. if (!NetworkClient.active)
  289. {
  290. Debug.LogError($"Command {functionFullName} called on {name} without an active client.", gameObject);
  291. return;
  292. }
  293. // previously we used NetworkClient.readyConnection.
  294. // now we check .ready separately.
  295. if (!NetworkClient.ready)
  296. {
  297. // Unreliable Cmds from NetworkTransform may be generated,
  298. // or client may have been set NotReady intentionally, so
  299. // only warn if on the reliable channel.
  300. if (channelId == Channels.Reliable)
  301. Debug.LogWarning($"Command {functionFullName} called on {name} while NetworkClient is not ready.\nThis may be ignored if client intentionally set NotReady.", gameObject);
  302. return;
  303. }
  304. // local players can always send commands, regardless of authority,
  305. // other objects must have authority.
  306. if (!(!requiresAuthority || isLocalPlayer || isOwned))
  307. {
  308. Debug.LogWarning($"Command {functionFullName} called on {name} without authority.", gameObject);
  309. return;
  310. }
  311. // IMPORTANT: can't use .connectionToServer here because calling
  312. // a command on other objects is allowed if requireAuthority is
  313. // false. other objects don't have a .connectionToServer.
  314. // => so we always need to use NetworkClient.connection instead.
  315. // => see also: https://github.com/vis2k/Mirror/issues/2629
  316. if (NetworkClient.connection == null)
  317. {
  318. Debug.LogError($"Command {functionFullName} called on {name} with no client running.", gameObject);
  319. return;
  320. }
  321. // construct the message
  322. CommandMessage message = new CommandMessage
  323. {
  324. netId = netId,
  325. componentIndex = ComponentIndex,
  326. // type+func so Inventory.RpcUse != Equipment.RpcUse
  327. functionHash = (ushort)functionHashCode,
  328. // segment to avoid reader allocations
  329. payload = writer.ToArraySegment()
  330. };
  331. // IMPORTANT: can't use .connectionToServer here because calling
  332. // a command on other objects is allowed if requireAuthority is
  333. // false. other objects don't have a .connectionToServer.
  334. // => so we always need to use NetworkClient.connection instead.
  335. // => see also: https://github.com/vis2k/Mirror/issues/2629
  336. // This bypasses the null check in NetworkClient.Send but we have
  337. // a null check above with a detailed error log.
  338. NetworkClient.connection.Send(message, channelId);
  339. }
  340. // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
  341. protected void SendRPCInternal(string functionFullName, int functionHashCode, NetworkWriter writer, int channelId, bool includeOwner)
  342. {
  343. // this was in Weaver before
  344. if (!NetworkServer.active)
  345. {
  346. Debug.LogError($"RPC Function {functionFullName} called without an active server.", gameObject);
  347. return;
  348. }
  349. // This cannot use NetworkServer.active, as that is not specific to this object.
  350. if (!isServer)
  351. {
  352. Debug.LogWarning($"ClientRpc {functionFullName} called on un-spawned object: {name}", gameObject);
  353. return;
  354. }
  355. // construct the message
  356. RpcMessage message = new RpcMessage
  357. {
  358. netId = netId,
  359. componentIndex = ComponentIndex,
  360. // type+func so Inventory.RpcUse != Equipment.RpcUse
  361. functionHash = (ushort)functionHashCode,
  362. // segment to avoid reader allocations
  363. payload = writer.ToArraySegment()
  364. };
  365. // serialize it to each ready observer's connection's rpc buffer.
  366. // send them all at once, instead of sending one message per rpc.
  367. // NetworkServer.SendToReadyObservers(netIdentity, message, includeOwner, channelId);
  368. // safety check used to be in SendToReadyObservers. keep it for now.
  369. if (netIdentity.observers == null || netIdentity.observers.Count == 0)
  370. return;
  371. // serialize the message only once
  372. using (NetworkWriterPooled serialized = NetworkWriterPool.Get())
  373. {
  374. serialized.Write(message);
  375. // send to every observer.
  376. // batching buffers this automatically.
  377. foreach (NetworkConnectionToClient conn in netIdentity.observers.Values)
  378. {
  379. bool isOwner = conn == netIdentity.connectionToClient;
  380. if ((!isOwner || includeOwner) && conn.isReady)
  381. {
  382. conn.Send(message, channelId);
  383. }
  384. }
  385. }
  386. }
  387. // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
  388. protected void SendTargetRPCInternal(NetworkConnection conn, string functionFullName, int functionHashCode, NetworkWriter writer, int channelId)
  389. {
  390. if (!NetworkServer.active)
  391. {
  392. Debug.LogError($"TargetRPC {functionFullName} was called on {name} when server not active.", gameObject);
  393. return;
  394. }
  395. if (!isServer)
  396. {
  397. Debug.LogWarning($"TargetRpc {functionFullName} called on {name} but that object has not been spawned or has been unspawned.", gameObject);
  398. return;
  399. }
  400. // connection parameter is optional. assign if null.
  401. if (conn is null)
  402. {
  403. conn = connectionToClient;
  404. }
  405. // if still null
  406. if (conn is null)
  407. {
  408. Debug.LogError($"TargetRPC {functionFullName} can't be sent because it was given a null connection. Make sure {name} is owned by a connection, or if you pass a connection manually then make sure it's not null. For example, TargetRpcs can be called on Player/Pet which are owned by a connection. However, they can not be called on Monsters/Npcs which don't have an owner connection.", gameObject);
  409. return;
  410. }
  411. // TODO change conn type to NetworkConnectionToClient to begin with.
  412. if (!(conn is NetworkConnectionToClient connToClient))
  413. {
  414. Debug.LogError($"TargetRPC {functionFullName} called on {name} requires a NetworkConnectionToClient but was given {conn.GetType().Name}", gameObject);
  415. return;
  416. }
  417. // construct the message
  418. RpcMessage message = new RpcMessage
  419. {
  420. netId = netId,
  421. componentIndex = ComponentIndex,
  422. // type+func so Inventory.RpcUse != Equipment.RpcUse
  423. functionHash = (ushort)functionHashCode,
  424. // segment to avoid reader allocations
  425. payload = writer.ToArraySegment()
  426. };
  427. // send it to the connection.
  428. // batching buffers this automatically.
  429. conn.Send(message, channelId);
  430. }
  431. // move the [SyncVar] generated property's .set into C# to avoid much IL
  432. //
  433. // public int health = 42;
  434. //
  435. // public int Networkhealth
  436. // {
  437. // get
  438. // {
  439. // return health;
  440. // }
  441. // [param: In]
  442. // set
  443. // {
  444. // if (!NetworkBehaviour.SyncVarEqual(value, ref health))
  445. // {
  446. // int oldValue = health;
  447. // SetSyncVar(value, ref health, 1uL);
  448. // if (NetworkServer.activeHost && !GetSyncVarHookGuard(1uL))
  449. // {
  450. // SetSyncVarHookGuard(1uL, value: true);
  451. // OnChanged(oldValue, value);
  452. // SetSyncVarHookGuard(1uL, value: false);
  453. // }
  454. // }
  455. // }
  456. // }
  457. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  458. public void GeneratedSyncVarSetter<T>(T value, ref T field, ulong dirtyBit, Action<T, T> OnChanged)
  459. {
  460. if (!SyncVarEqual(value, ref field))
  461. {
  462. T oldValue = field;
  463. SetSyncVar(value, ref field, dirtyBit);
  464. // call hook (if any)
  465. if (OnChanged != null)
  466. {
  467. // in host mode, setting a SyncVar calls the hook directly.
  468. // in client-only mode, OnDeserialize would call it.
  469. // we use hook guard to protect against deadlock where hook
  470. // changes syncvar, calling hook again.
  471. if (NetworkServer.activeHost && !GetSyncVarHookGuard(dirtyBit))
  472. {
  473. SetSyncVarHookGuard(dirtyBit, true);
  474. OnChanged(oldValue, value);
  475. SetSyncVarHookGuard(dirtyBit, false);
  476. }
  477. }
  478. }
  479. }
  480. // GameObject needs custom handling for persistence via netId.
  481. // has one extra parameter.
  482. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  483. public void GeneratedSyncVarSetter_GameObject(GameObject value, ref GameObject field, ulong dirtyBit, Action<GameObject, GameObject> OnChanged, ref uint netIdField)
  484. {
  485. if (!SyncVarGameObjectEqual(value, netIdField))
  486. {
  487. GameObject oldValue = field;
  488. SetSyncVarGameObject(value, ref field, dirtyBit, ref netIdField);
  489. // call hook (if any)
  490. if (OnChanged != null)
  491. {
  492. // in host mode, setting a SyncVar calls the hook directly.
  493. // in client-only mode, OnDeserialize would call it.
  494. // we use hook guard to protect against deadlock where hook
  495. // changes syncvar, calling hook again.
  496. if (NetworkServer.activeHost && !GetSyncVarHookGuard(dirtyBit))
  497. {
  498. SetSyncVarHookGuard(dirtyBit, true);
  499. OnChanged(oldValue, value);
  500. SetSyncVarHookGuard(dirtyBit, false);
  501. }
  502. }
  503. }
  504. }
  505. // NetworkIdentity needs custom handling for persistence via netId.
  506. // has one extra parameter.
  507. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  508. public void GeneratedSyncVarSetter_NetworkIdentity(NetworkIdentity value, ref NetworkIdentity field, ulong dirtyBit, Action<NetworkIdentity, NetworkIdentity> OnChanged, ref uint netIdField)
  509. {
  510. if (!SyncVarNetworkIdentityEqual(value, netIdField))
  511. {
  512. NetworkIdentity oldValue = field;
  513. SetSyncVarNetworkIdentity(value, ref field, dirtyBit, ref netIdField);
  514. // call hook (if any)
  515. if (OnChanged != null)
  516. {
  517. // in host mode, setting a SyncVar calls the hook directly.
  518. // in client-only mode, OnDeserialize would call it.
  519. // we use hook guard to protect against deadlock where hook
  520. // changes syncvar, calling hook again.
  521. if (NetworkServer.activeHost && !GetSyncVarHookGuard(dirtyBit))
  522. {
  523. SetSyncVarHookGuard(dirtyBit, true);
  524. OnChanged(oldValue, value);
  525. SetSyncVarHookGuard(dirtyBit, false);
  526. }
  527. }
  528. }
  529. }
  530. // NetworkBehaviour needs custom handling for persistence via netId.
  531. // has one extra parameter.
  532. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  533. public void GeneratedSyncVarSetter_NetworkBehaviour<T>(T value, ref T field, ulong dirtyBit, Action<T, T> OnChanged, ref NetworkBehaviourSyncVar netIdField)
  534. where T : NetworkBehaviour
  535. {
  536. if (!SyncVarNetworkBehaviourEqual(value, netIdField))
  537. {
  538. T oldValue = field;
  539. SetSyncVarNetworkBehaviour(value, ref field, dirtyBit, ref netIdField);
  540. // call hook (if any)
  541. if (OnChanged != null)
  542. {
  543. // in host mode, setting a SyncVar calls the hook directly.
  544. // in client-only mode, OnDeserialize would call it.
  545. // we use hook guard to protect against deadlock where hook
  546. // changes syncvar, calling hook again.
  547. if (NetworkServer.activeHost && !GetSyncVarHookGuard(dirtyBit))
  548. {
  549. SetSyncVarHookGuard(dirtyBit, true);
  550. OnChanged(oldValue, value);
  551. SetSyncVarHookGuard(dirtyBit, false);
  552. }
  553. }
  554. }
  555. }
  556. // helper function for [SyncVar] GameObjects.
  557. // needs to be public so that tests & NetworkBehaviours from other
  558. // assemblies both find it
  559. [EditorBrowsable(EditorBrowsableState.Never)]
  560. public static bool SyncVarGameObjectEqual(GameObject newGameObject, uint netIdField)
  561. {
  562. uint newNetId = 0;
  563. if (newGameObject != null)
  564. {
  565. if (newGameObject.TryGetComponent(out NetworkIdentity identity))
  566. {
  567. newNetId = identity.netId;
  568. if (newNetId == 0)
  569. {
  570. Debug.LogWarning($"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?");
  571. }
  572. }
  573. }
  574. return newNetId == netIdField;
  575. }
  576. // helper function for [SyncVar] GameObjects.
  577. // dirtyBit is a mask like 00010
  578. protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, ulong dirtyBit, ref uint netIdField)
  579. {
  580. if (GetSyncVarHookGuard(dirtyBit))
  581. return;
  582. uint newNetId = 0;
  583. if (newGameObject != null)
  584. {
  585. if (newGameObject.TryGetComponent(out NetworkIdentity identity))
  586. {
  587. newNetId = identity.netId;
  588. if (newNetId == 0)
  589. {
  590. Debug.LogWarning($"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?");
  591. }
  592. }
  593. }
  594. //Debug.Log($"SetSyncVar GameObject {GetType().Name} bit:{dirtyBit} netfieldId:{netIdField} -> {newNetId}");
  595. SetSyncVarDirtyBit(dirtyBit);
  596. // assign new one on the server, and in case we ever need it on client too
  597. gameObjectField = newGameObject;
  598. netIdField = newNetId;
  599. }
  600. // helper function for [SyncVar] GameObjects.
  601. // -> ref GameObject as second argument makes OnDeserialize processing easier
  602. protected GameObject GetSyncVarGameObject(uint netId, ref GameObject gameObjectField)
  603. {
  604. // server always uses the field
  605. // if neither, fallback to original field
  606. // fixes: https://github.com/MirrorNetworking/Mirror/issues/3447
  607. if (isServer || !isClient)
  608. {
  609. return gameObjectField;
  610. }
  611. // client always looks up based on netId because objects might get in and out of range
  612. // over and over again, which shouldn't null them forever
  613. if (NetworkClient.spawned.TryGetValue(netId, out NetworkIdentity identity) && identity != null)
  614. return gameObjectField = identity.gameObject;
  615. return null;
  616. }
  617. // helper function for [SyncVar] NetworkIdentities.
  618. // needs to be public so that tests & NetworkBehaviours from other
  619. // assemblies both find it
  620. [EditorBrowsable(EditorBrowsableState.Never)]
  621. public static bool SyncVarNetworkIdentityEqual(NetworkIdentity newIdentity, uint netIdField)
  622. {
  623. uint newNetId = 0;
  624. if (newIdentity != null)
  625. {
  626. newNetId = newIdentity.netId;
  627. if (newNetId == 0)
  628. {
  629. Debug.LogWarning($"SetSyncVarNetworkIdentity NetworkIdentity {newIdentity} has a zero netId. Maybe it is not spawned yet?");
  630. }
  631. }
  632. // netId changed?
  633. return newNetId == netIdField;
  634. }
  635. // move the [SyncVar] generated OnDeserialize C# to avoid much IL.
  636. //
  637. // before:
  638. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  639. // {
  640. // base.DeserializeSyncVars(reader, initialState);
  641. // if (initialState)
  642. // {
  643. // int num = health;
  644. // Networkhealth = reader.ReadInt();
  645. // if (!NetworkBehaviour.SyncVarEqual(num, ref health))
  646. // {
  647. // OnChanged(num, health);
  648. // }
  649. // return;
  650. // }
  651. // long num2 = (long)reader.ReadULong();
  652. // if ((num2 & 1L) != 0L)
  653. // {
  654. // int num3 = health;
  655. // Networkhealth = reader.ReadInt();
  656. // if (!NetworkBehaviour.SyncVarEqual(num3, ref health))
  657. // {
  658. // OnChanged(num3, health);
  659. // }
  660. // }
  661. // }
  662. //
  663. // after:
  664. //
  665. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  666. // {
  667. // base.DeserializeSyncVars(reader, initialState);
  668. // if (initialState)
  669. // {
  670. // GeneratedSyncVarDeserialize(reader, ref health, null, reader.ReadInt());
  671. // return;
  672. // }
  673. // long num = (long)reader.ReadULong();
  674. // if ((num & 1L) != 0L)
  675. // {
  676. // GeneratedSyncVarDeserialize(reader, ref health, null, reader.ReadInt());
  677. // }
  678. // }
  679. public void GeneratedSyncVarDeserialize<T>(ref T field, Action<T, T> OnChanged, T value)
  680. {
  681. T previous = field;
  682. field = value;
  683. // any hook? then call if changed.
  684. if (OnChanged != null && !SyncVarEqual(previous, ref field))
  685. {
  686. OnChanged(previous, field);
  687. }
  688. }
  689. // move the [SyncVar] generated OnDeserialize C# to avoid much IL.
  690. //
  691. // before:
  692. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  693. // {
  694. // base.DeserializeSyncVars(reader, initialState);
  695. // if (initialState)
  696. // {
  697. // uint __targetNetId = ___targetNetId;
  698. // GameObject networktarget = Networktarget;
  699. // ___targetNetId = reader.ReadUInt();
  700. // if (!NetworkBehaviour.SyncVarEqual(__targetNetId, ref ___targetNetId))
  701. // {
  702. // OnChangedNB(networktarget, Networktarget);
  703. // }
  704. // return;
  705. // }
  706. // long num = (long)reader.ReadULong();
  707. // if ((num & 1L) != 0L)
  708. // {
  709. // uint __targetNetId2 = ___targetNetId;
  710. // GameObject networktarget2 = Networktarget;
  711. // ___targetNetId = reader.ReadUInt();
  712. // if (!NetworkBehaviour.SyncVarEqual(__targetNetId2, ref ___targetNetId))
  713. // {
  714. // OnChangedNB(networktarget2, Networktarget);
  715. // }
  716. // }
  717. // }
  718. //
  719. // after:
  720. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  721. // {
  722. // base.DeserializeSyncVars(reader, initialState);
  723. // if (initialState)
  724. // {
  725. // GeneratedSyncVarDeserialize_GameObject(reader, ref target, OnChangedNB, ref ___targetNetId);
  726. // return;
  727. // }
  728. // long num = (long)reader.ReadULong();
  729. // if ((num & 1L) != 0L)
  730. // {
  731. // GeneratedSyncVarDeserialize_GameObject(reader, ref target, OnChangedNB, ref ___targetNetId);
  732. // }
  733. // }
  734. public void GeneratedSyncVarDeserialize_GameObject(ref GameObject field, Action<GameObject, GameObject> OnChanged, NetworkReader reader, ref uint netIdField)
  735. {
  736. uint previousNetId = netIdField;
  737. GameObject previousGameObject = field;
  738. netIdField = reader.ReadUInt();
  739. // get the new GameObject now that netId field is set
  740. field = GetSyncVarGameObject(netIdField, ref field);
  741. // any hook? then call if changed.
  742. if (OnChanged != null && !SyncVarEqual(previousNetId, ref netIdField))
  743. {
  744. OnChanged(previousGameObject, field);
  745. }
  746. }
  747. // move the [SyncVar] generated OnDeserialize C# to avoid much IL.
  748. //
  749. // before:
  750. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  751. // {
  752. // base.DeserializeSyncVars(reader, initialState);
  753. // if (initialState)
  754. // {
  755. // uint __targetNetId = ___targetNetId;
  756. // NetworkIdentity networktarget = Networktarget;
  757. // ___targetNetId = reader.ReadUInt();
  758. // if (!NetworkBehaviour.SyncVarEqual(__targetNetId, ref ___targetNetId))
  759. // {
  760. // OnChangedNI(networktarget, Networktarget);
  761. // }
  762. // return;
  763. // }
  764. // long num = (long)reader.ReadULong();
  765. // if ((num & 1L) != 0L)
  766. // {
  767. // uint __targetNetId2 = ___targetNetId;
  768. // NetworkIdentity networktarget2 = Networktarget;
  769. // ___targetNetId = reader.ReadUInt();
  770. // if (!NetworkBehaviour.SyncVarEqual(__targetNetId2, ref ___targetNetId))
  771. // {
  772. // OnChangedNI(networktarget2, Networktarget);
  773. // }
  774. // }
  775. // }
  776. //
  777. // after:
  778. //
  779. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  780. // {
  781. // base.DeserializeSyncVars(reader, initialState);
  782. // if (initialState)
  783. // {
  784. // GeneratedSyncVarDeserialize_NetworkIdentity(reader, ref target, OnChangedNI, ref ___targetNetId);
  785. // return;
  786. // }
  787. // long num = (long)reader.ReadULong();
  788. // if ((num & 1L) != 0L)
  789. // {
  790. // GeneratedSyncVarDeserialize_NetworkIdentity(reader, ref target, OnChangedNI, ref ___targetNetId);
  791. // }
  792. // }
  793. public void GeneratedSyncVarDeserialize_NetworkIdentity(ref NetworkIdentity field, Action<NetworkIdentity, NetworkIdentity> OnChanged, NetworkReader reader, ref uint netIdField)
  794. {
  795. uint previousNetId = netIdField;
  796. NetworkIdentity previousIdentity = field;
  797. netIdField = reader.ReadUInt();
  798. // get the new NetworkIdentity now that netId field is set
  799. field = GetSyncVarNetworkIdentity(netIdField, ref field);
  800. // any hook? then call if changed.
  801. if (OnChanged != null && !SyncVarEqual(previousNetId, ref netIdField))
  802. {
  803. OnChanged(previousIdentity, field);
  804. }
  805. }
  806. // move the [SyncVar] generated OnDeserialize C# to avoid much IL.
  807. //
  808. // before:
  809. //
  810. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  811. // {
  812. // base.DeserializeSyncVars(reader, initialState);
  813. // if (initialState)
  814. // {
  815. // NetworkBehaviourSyncVar __targetNetId = ___targetNetId;
  816. // Tank networktarget = Networktarget;
  817. // ___targetNetId = reader.ReadNetworkBehaviourSyncVar();
  818. // if (!NetworkBehaviour.SyncVarEqual(__targetNetId, ref ___targetNetId))
  819. // {
  820. // OnChangedNB(networktarget, Networktarget);
  821. // }
  822. // return;
  823. // }
  824. // long num = (long)reader.ReadULong();
  825. // if ((num & 1L) != 0L)
  826. // {
  827. // NetworkBehaviourSyncVar __targetNetId2 = ___targetNetId;
  828. // Tank networktarget2 = Networktarget;
  829. // ___targetNetId = reader.ReadNetworkBehaviourSyncVar();
  830. // if (!NetworkBehaviour.SyncVarEqual(__targetNetId2, ref ___targetNetId))
  831. // {
  832. // OnChangedNB(networktarget2, Networktarget);
  833. // }
  834. // }
  835. // }
  836. //
  837. // after:
  838. //
  839. // public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
  840. // {
  841. // base.DeserializeSyncVars(reader, initialState);
  842. // if (initialState)
  843. // {
  844. // GeneratedSyncVarDeserialize_NetworkBehaviour(reader, ref target, OnChangedNB, ref ___targetNetId);
  845. // return;
  846. // }
  847. // long num = (long)reader.ReadULong();
  848. // if ((num & 1L) != 0L)
  849. // {
  850. // GeneratedSyncVarDeserialize_NetworkBehaviour(reader, ref target, OnChangedNB, ref ___targetNetId);
  851. // }
  852. // }
  853. public void GeneratedSyncVarDeserialize_NetworkBehaviour<T>(ref T field, Action<T, T> OnChanged, NetworkReader reader, ref NetworkBehaviourSyncVar netIdField)
  854. where T : NetworkBehaviour
  855. {
  856. NetworkBehaviourSyncVar previousNetId = netIdField;
  857. T previousBehaviour = field;
  858. netIdField = reader.ReadNetworkBehaviourSyncVar();
  859. // get the new NetworkBehaviour now that netId field is set
  860. field = GetSyncVarNetworkBehaviour(netIdField, ref field);
  861. // any hook? then call if changed.
  862. if (OnChanged != null && !SyncVarEqual(previousNetId, ref netIdField))
  863. {
  864. OnChanged(previousBehaviour, field);
  865. }
  866. }
  867. // helper function for [SyncVar] NetworkIdentities.
  868. // dirtyBit is a mask like 00010
  869. protected void SetSyncVarNetworkIdentity(NetworkIdentity newIdentity, ref NetworkIdentity identityField, ulong dirtyBit, ref uint netIdField)
  870. {
  871. if (GetSyncVarHookGuard(dirtyBit))
  872. return;
  873. uint newNetId = 0;
  874. if (newIdentity != null)
  875. {
  876. newNetId = newIdentity.netId;
  877. if (newNetId == 0)
  878. {
  879. Debug.LogWarning($"SetSyncVarNetworkIdentity NetworkIdentity {newIdentity} has a zero netId. Maybe it is not spawned yet?");
  880. }
  881. }
  882. //Debug.Log($"SetSyncVarNetworkIdentity NetworkIdentity {GetType().Name} bit:{dirtyBit} netIdField:{netIdField} -> {newNetId}");
  883. SetSyncVarDirtyBit(dirtyBit);
  884. netIdField = newNetId;
  885. // assign new one on the server, and in case we ever need it on client too
  886. identityField = newIdentity;
  887. }
  888. // helper function for [SyncVar] NetworkIdentities.
  889. // -> ref GameObject as second argument makes OnDeserialize processing easier
  890. protected NetworkIdentity GetSyncVarNetworkIdentity(uint netId, ref NetworkIdentity identityField)
  891. {
  892. // server always uses the field
  893. // if neither, fallback to original field
  894. // fixes: https://github.com/MirrorNetworking/Mirror/issues/3447
  895. if (isServer || !isClient)
  896. {
  897. return identityField;
  898. }
  899. // client always looks up based on netId because objects might get in and out of range
  900. // over and over again, which shouldn't null them forever
  901. NetworkClient.spawned.TryGetValue(netId, out identityField);
  902. return identityField;
  903. }
  904. protected static bool SyncVarNetworkBehaviourEqual<T>(T newBehaviour, NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour
  905. {
  906. uint newNetId = 0;
  907. byte newComponentIndex = 0;
  908. if (newBehaviour != null)
  909. {
  910. newNetId = newBehaviour.netId;
  911. newComponentIndex = newBehaviour.ComponentIndex;
  912. if (newNetId == 0)
  913. {
  914. Debug.LogWarning($"SetSyncVarNetworkIdentity NetworkIdentity {newBehaviour} has a zero netId. Maybe it is not spawned yet?");
  915. }
  916. }
  917. // netId changed?
  918. return syncField.Equals(newNetId, newComponentIndex);
  919. }
  920. // helper function for [SyncVar] NetworkIdentities.
  921. // dirtyBit is a mask like 00010
  922. protected void SetSyncVarNetworkBehaviour<T>(T newBehaviour, ref T behaviourField, ulong dirtyBit, ref NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour
  923. {
  924. if (GetSyncVarHookGuard(dirtyBit))
  925. return;
  926. uint newNetId = 0;
  927. byte componentIndex = 0;
  928. if (newBehaviour != null)
  929. {
  930. newNetId = newBehaviour.netId;
  931. componentIndex = newBehaviour.ComponentIndex;
  932. if (newNetId == 0)
  933. {
  934. Debug.LogWarning($"{nameof(SetSyncVarNetworkBehaviour)} NetworkIdentity {newBehaviour} has a zero netId. Maybe it is not spawned yet?");
  935. }
  936. }
  937. syncField = new NetworkBehaviourSyncVar(newNetId, componentIndex);
  938. SetSyncVarDirtyBit(dirtyBit);
  939. // assign new one on the server, and in case we ever need it on client too
  940. behaviourField = newBehaviour;
  941. // Debug.Log($"SetSyncVarNetworkBehaviour NetworkIdentity {GetType().Name} bit [{dirtyBit}] netIdField:{oldField}->{syncField}");
  942. }
  943. // helper function for [SyncVar] NetworkIdentities.
  944. // -> ref GameObject as second argument makes OnDeserialize processing easier
  945. protected T GetSyncVarNetworkBehaviour<T>(NetworkBehaviourSyncVar syncNetBehaviour, ref T behaviourField) where T : NetworkBehaviour
  946. {
  947. // server always uses the field
  948. // if neither, fallback to original field
  949. // fixes: https://github.com/MirrorNetworking/Mirror/issues/3447
  950. if (isServer || !isClient)
  951. {
  952. return behaviourField;
  953. }
  954. // client always looks up based on netId because objects might get in and out of range
  955. // over and over again, which shouldn't null them forever
  956. if (!NetworkClient.spawned.TryGetValue(syncNetBehaviour.netId, out NetworkIdentity identity))
  957. {
  958. return null;
  959. }
  960. behaviourField = identity.NetworkBehaviours[syncNetBehaviour.componentIndex] as T;
  961. return behaviourField;
  962. }
  963. protected static bool SyncVarEqual<T>(T value, ref T fieldValue)
  964. {
  965. // newly initialized or changed value?
  966. // value.Equals(fieldValue) allocates without 'where T : IEquatable'
  967. // seems like we use EqualityComparer to avoid allocations,
  968. // because not all SyncVars<T> are IEquatable
  969. return EqualityComparer<T>.Default.Equals(value, fieldValue);
  970. }
  971. // dirtyBit is a mask like 00010
  972. protected void SetSyncVar<T>(T value, ref T fieldValue, ulong dirtyBit)
  973. {
  974. //Debug.Log($"SetSyncVar {GetType().Name} bit:{dirtyBit} fieldValue:{value}");
  975. SetSyncVarDirtyBit(dirtyBit);
  976. fieldValue = value;
  977. }
  978. /// <summary>Override to do custom serialization (instead of SyncVars/SyncLists). Use OnDeserialize too.</summary>
  979. // if a class has syncvars, then OnSerialize/OnDeserialize are added
  980. // automatically.
  981. //
  982. // initialState is true for full spawns, false for delta syncs.
  983. // note: SyncVar hooks are only called when inital=false
  984. public virtual void OnSerialize(NetworkWriter writer, bool initialState)
  985. {
  986. SerializeSyncObjects(writer, initialState);
  987. SerializeSyncVars(writer, initialState);
  988. }
  989. /// <summary>Override to do custom deserialization (instead of SyncVars/SyncLists). Use OnSerialize too.</summary>
  990. public virtual void OnDeserialize(NetworkReader reader, bool initialState)
  991. {
  992. DeserializeSyncObjects(reader, initialState);
  993. DeserializeSyncVars(reader, initialState);
  994. }
  995. void SerializeSyncObjects(NetworkWriter writer, bool initialState)
  996. {
  997. // if initialState: write all SyncVars.
  998. // otherwise write dirtyBits+dirty SyncVars
  999. if (initialState)
  1000. SerializeObjectsAll(writer);
  1001. else
  1002. SerializeObjectsDelta(writer);
  1003. }
  1004. void DeserializeSyncObjects(NetworkReader reader, bool initialState)
  1005. {
  1006. if (initialState)
  1007. {
  1008. DeserializeObjectsAll(reader);
  1009. }
  1010. else
  1011. {
  1012. DeserializeObjectsDelta(reader);
  1013. }
  1014. }
  1015. // USED BY WEAVER
  1016. protected virtual void SerializeSyncVars(NetworkWriter writer, bool initialState)
  1017. {
  1018. // SyncVar are written here in subclass
  1019. // if initialState
  1020. // write all SyncVars
  1021. // else
  1022. // write syncVarDirtyBits
  1023. // write dirty SyncVars
  1024. }
  1025. // USED BY WEAVER
  1026. protected virtual void DeserializeSyncVars(NetworkReader reader, bool initialState)
  1027. {
  1028. // SyncVars are read here in subclass
  1029. // if initialState
  1030. // read all SyncVars
  1031. // else
  1032. // read syncVarDirtyBits
  1033. // read dirty SyncVars
  1034. }
  1035. public void SerializeObjectsAll(NetworkWriter writer)
  1036. {
  1037. for (int i = 0; i < syncObjects.Count; i++)
  1038. {
  1039. SyncObject syncObject = syncObjects[i];
  1040. syncObject.OnSerializeAll(writer);
  1041. }
  1042. }
  1043. public void SerializeObjectsDelta(NetworkWriter writer)
  1044. {
  1045. // write the mask
  1046. writer.WriteULong(syncObjectDirtyBits);
  1047. // serializable objects, such as synclists
  1048. for (int i = 0; i < syncObjects.Count; i++)
  1049. {
  1050. // check dirty mask at nth bit
  1051. SyncObject syncObject = syncObjects[i];
  1052. if ((syncObjectDirtyBits & (1UL << i)) != 0)
  1053. {
  1054. syncObject.OnSerializeDelta(writer);
  1055. }
  1056. }
  1057. }
  1058. internal void DeserializeObjectsAll(NetworkReader reader)
  1059. {
  1060. for (int i = 0; i < syncObjects.Count; i++)
  1061. {
  1062. SyncObject syncObject = syncObjects[i];
  1063. syncObject.OnDeserializeAll(reader);
  1064. }
  1065. }
  1066. internal void DeserializeObjectsDelta(NetworkReader reader)
  1067. {
  1068. ulong dirty = reader.ReadULong();
  1069. for (int i = 0; i < syncObjects.Count; i++)
  1070. {
  1071. // check dirty mask at nth bit
  1072. SyncObject syncObject = syncObjects[i];
  1073. if ((dirty & (1UL << i)) != 0)
  1074. {
  1075. syncObject.OnDeserializeDelta(reader);
  1076. }
  1077. }
  1078. }
  1079. // safely serialize each component in a way that one reading too much or
  1080. // too few bytes will show obvious, easy to resolve error messages.
  1081. //
  1082. // prevents the original UNET bug which started Mirror:
  1083. // https://github.com/vis2k/Mirror/issues/2617
  1084. // where one component would read too much, and then all following reads
  1085. // on other entities would be mismatched, causing the weirdest errors.
  1086. //
  1087. // reads <<len, payload, len, payload, ...>> for 100% safety.
  1088. internal void Serialize(NetworkWriter writer, bool initialState)
  1089. {
  1090. // reserve length header to ensure the correct amount will be read.
  1091. // originally we used a 4 byte header (too bandwidth heavy).
  1092. // instead, let's "& 0xFF" the size.
  1093. //
  1094. // this is cleaner than barriers at the end of payload, because:
  1095. // - ensures the correct safety is read _before_ payload.
  1096. // - it's quite hard to break the check.
  1097. // a component would need to read/write the intented amount
  1098. // multiplied by 255 in order to miss the check.
  1099. // with barriers, reading 1 byte too much may still succeed if the
  1100. // next component's first byte matches the expected barrier.
  1101. // - we can still attempt to correct the invalid position via the
  1102. // safety length byte (we know that one is correct).
  1103. //
  1104. // it's just overall cleaner, and still low on bandwidth.
  1105. // write placeholder length byte
  1106. // (jumping back later is WAY faster than allocating a temporary
  1107. // writer for the payload, then writing payload.size, payload)
  1108. int headerPosition = writer.Position;
  1109. writer.WriteByte(0);
  1110. int contentPosition = writer.Position;
  1111. // write payload
  1112. try
  1113. {
  1114. // note this may not write anything if no syncIntervals elapsed
  1115. OnSerialize(writer, initialState);
  1116. }
  1117. catch (Exception e)
  1118. {
  1119. // show a detailed error and let the user know what went wrong
  1120. Debug.LogError($"OnSerialize failed for: object={name} component={GetType()} sceneId={netIdentity.sceneId:X}\n\n{e}");
  1121. }
  1122. int endPosition = writer.Position;
  1123. // fill in length hash as the last byte of the 4 byte length
  1124. writer.Position = headerPosition;
  1125. int size = endPosition - contentPosition;
  1126. byte safety = (byte)(size & 0xFF);
  1127. writer.WriteByte(safety);
  1128. writer.Position = endPosition;
  1129. //Debug.Log($"OnSerializeSafely written for object {name} component:{GetType()} sceneId:{sceneId:X} header:{headerPosition} content:{contentPosition} end:{endPosition} contentSize:{endPosition - contentPosition}");
  1130. }
  1131. // correct the read size with the 1 byte length hash (by mischa).
  1132. // -> the component most likely read a few too many/few bytes.
  1133. // -> we know the correct last byte of the expected size (=the safety).
  1134. // -> attempt to reconstruct the size via safety byte.
  1135. // it will be correct unless someone wrote way way too much,
  1136. // as in > 255 bytes worth too much.
  1137. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1138. internal static int ErrorCorrection(int size, byte safety)
  1139. {
  1140. // clear the last byte which most likely contains the error
  1141. uint cleared = (uint)size & 0xFFFFFF00;
  1142. // insert the safety which we know to be correct
  1143. return (int)(cleared | safety);
  1144. }
  1145. // returns false in case of errors.
  1146. // server needs to know in order to disconnect on error.
  1147. internal bool Deserialize(NetworkReader reader, bool initialState)
  1148. {
  1149. // detect errors, but attempt to correct before returning
  1150. bool result = true;
  1151. // read 1 byte length hash safety & capture beginning for size check
  1152. byte safety = reader.ReadByte();
  1153. int chunkStart = reader.Position;
  1154. // call OnDeserialize and wrap it in a try-catch block so there's no
  1155. // way to mess up another component's deserialization
  1156. try
  1157. {
  1158. //Debug.Log($"OnDeserializeSafely: {name} component:{GetType()} sceneId:{sceneId:X} length:{contentSize}");
  1159. OnDeserialize(reader, initialState);
  1160. }
  1161. catch (Exception e)
  1162. {
  1163. // show a detailed error and let the user know what went wrong
  1164. Debug.LogError($"OnDeserialize failed Exception={e.GetType()} (see below) object={name} component={GetType()} netId={netId}. Possible Reasons:\n" +
  1165. $" * Do {GetType()}'s OnSerialize and OnDeserialize calls write the same amount of data? \n" +
  1166. $" * Was there an exception in {GetType()}'s OnSerialize/OnDeserialize code?\n" +
  1167. $" * Are the server and client the exact same project?\n" +
  1168. $" * 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" +
  1169. $"Exception {e}");
  1170. result = false;
  1171. }
  1172. // compare bytes read with length hash
  1173. int size = reader.Position - chunkStart;
  1174. byte sizeHash = (byte)(size & 0xFF);
  1175. if (sizeHash != safety)
  1176. {
  1177. // warn the user.
  1178. Debug.LogWarning($"{name} (netId={netId}): {GetType()} OnDeserialize size mismatch. It read {size} bytes, which caused a size hash mismatch of {sizeHash:X2} vs. {safety:X2}. Make sure that OnSerialize and OnDeserialize write/read the same amount of data in all cases.");
  1179. // attempt to fix the position, so the following components
  1180. // don't all fail. this is very likely to work, unless the user
  1181. // read more than 255 bytes too many / too few.
  1182. //
  1183. // see test: SerializationSizeMismatch.
  1184. int correctedSize = ErrorCorrection(size, safety);
  1185. reader.Position = chunkStart + correctedSize;
  1186. result = false;
  1187. }
  1188. return result;
  1189. }
  1190. internal void ResetSyncObjects()
  1191. {
  1192. foreach (SyncObject syncObject in syncObjects)
  1193. {
  1194. syncObject.Reset();
  1195. }
  1196. }
  1197. /// <summary>Like Start(), but only called on server and host.</summary>
  1198. public virtual void OnStartServer() {}
  1199. /// <summary>Stop event, only called on server and host.</summary>
  1200. public virtual void OnStopServer() {}
  1201. /// <summary>Like Start(), but only called on client and host.</summary>
  1202. public virtual void OnStartClient() {}
  1203. /// <summary>Stop event, only called on client and host.</summary>
  1204. public virtual void OnStopClient() {}
  1205. /// <summary>Like Start(), but only called on client and host for the local player object.</summary>
  1206. public virtual void OnStartLocalPlayer() {}
  1207. /// <summary>Stop event, but only called on client and host for the local player object.</summary>
  1208. public virtual void OnStopLocalPlayer() {}
  1209. /// <summary>Like Start(), but only called for objects the client has authority over.</summary>
  1210. public virtual void OnStartAuthority() {}
  1211. /// <summary>Stop event, only called for objects the client has authority over.</summary>
  1212. public virtual void OnStopAuthority() {}
  1213. // Weaver injects this into inheriting classes to return true.
  1214. // allows runtime & tests to check if a type was weaved.
  1215. [EditorBrowsable(EditorBrowsableState.Never)]
  1216. public virtual bool Weaved() => false;
  1217. }
  1218. }