SyncVarNetworkBehaviour.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // persistent NetworkBehaviour SyncField which stores netId and component index.
  2. // this is necessary for cases like a player's target.
  3. // the target might run in and out of visibility range and become 'null'.
  4. // but the 'netId' remains and will always point to the monster if around.
  5. // (we also store the component index because GameObject can have multiple
  6. // NetworkBehaviours of same type)
  7. //
  8. // original Weaver code was broken because it didn't store by netId.
  9. using System;
  10. using System.Runtime.CompilerServices;
  11. namespace Mirror
  12. {
  13. // SyncField<NetworkBehaviour> needs an uint netId and a byte componentIndex.
  14. // we use an ulong SyncField internally to store both.
  15. // while providing .spawned lookup for convenience.
  16. // NOTE: server always knows all spawned. consider caching the field again.
  17. // <T> to support abstract NetworkBehaviour and classes inheriting from it.
  18. // => hooks can be OnHook(Monster, Monster) instead of OnHook(NB, NB)
  19. // => implicit cast can be to/from Monster instead of only NetworkBehaviour
  20. // => Weaver needs explicit types for hooks too, not just OnHook(NB, NB)
  21. public class SyncVarNetworkBehaviour<T> : SyncVar<ulong>
  22. where T : NetworkBehaviour
  23. {
  24. // .spawned lookup from netId overwrites base uint .Value
  25. public new T Value
  26. {
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. get => ULongToNetworkBehaviour(base.Value);
  29. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  30. set => base.Value = NetworkBehaviourToULong(value);
  31. }
  32. // OnChanged Callback is for <uint, uint>.
  33. // Let's also have one for <NetworkBehaviour, NetworkBehaviour>
  34. public new event Action<T, T> Callback;
  35. // overwrite CallCallback to use the NetworkIdentity version instead
  36. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  37. protected override void InvokeCallback(ulong oldValue, ulong newValue) =>
  38. Callback?.Invoke(ULongToNetworkBehaviour(oldValue), ULongToNetworkBehaviour(newValue));
  39. // ctor
  40. // 'value = null' so we can do:
  41. // SyncVarNetworkBehaviour = new SyncVarNetworkBehaviour()
  42. // instead of
  43. // SyncVarNetworkBehaviour = new SyncVarNetworkBehaviour(null);
  44. public SyncVarNetworkBehaviour(T value = null)
  45. : base(NetworkBehaviourToULong(value)) {}
  46. // implicit conversion: NetworkBehaviour value = SyncFieldNetworkBehaviour
  47. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  48. public static implicit operator T(SyncVarNetworkBehaviour<T> field) => field.Value;
  49. // implicit conversion: SyncFieldNetworkBehaviour = value
  50. // even if SyncField is readonly, it's still useful: SyncFieldNetworkBehaviour = target;
  51. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  52. public static implicit operator SyncVarNetworkBehaviour<T>(T value) => new SyncVarNetworkBehaviour<T>(value);
  53. // NOTE: overloading all == operators blocks '== null' checks with an
  54. // "ambiguous invocation" error. that's good. this way user code like
  55. // "player.target == null" won't compile instead of silently failing!
  56. // == operator for comparisons like Player.target==monster
  57. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  58. public static bool operator ==(SyncVarNetworkBehaviour<T> a, SyncVarNetworkBehaviour<T> b) =>
  59. a.Value == b.Value;
  60. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  61. public static bool operator !=(SyncVarNetworkBehaviour<T> a, SyncVarNetworkBehaviour<T> b) => !(a == b);
  62. // == operator for comparisons like Player.target==monster
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. public static bool operator ==(SyncVarNetworkBehaviour<T> a, NetworkBehaviour b) =>
  65. a.Value == b;
  66. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  67. public static bool operator !=(SyncVarNetworkBehaviour<T> a, NetworkBehaviour b) => !(a == b);
  68. // == operator for comparisons like Player.target==monster
  69. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  70. public static bool operator ==(SyncVarNetworkBehaviour<T> a, T b) =>
  71. a.Value == b;
  72. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  73. public static bool operator !=(SyncVarNetworkBehaviour<T> a, T b) => !(a == b);
  74. // == operator for comparisons like Player.target==monster
  75. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  76. public static bool operator ==(NetworkBehaviour a, SyncVarNetworkBehaviour<T> b) =>
  77. a == b.Value;
  78. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  79. public static bool operator !=(NetworkBehaviour a, SyncVarNetworkBehaviour<T> b) => !(a == b);
  80. // == operator for comparisons like Player.target==monster
  81. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  82. public static bool operator ==(T a, SyncVarNetworkBehaviour<T> b) =>
  83. a == b.Value;
  84. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  85. public static bool operator !=(T a, SyncVarNetworkBehaviour<T> b) => !(a == b);
  86. // if we overwrite == operators, we also need to overwrite .Equals.
  87. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  88. public override bool Equals(object obj) => obj is SyncVarNetworkBehaviour<T> value && this == value;
  89. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  90. public override int GetHashCode() => Value.GetHashCode();
  91. // helper functions to get/set netId, componentIndex from ulong
  92. // netId on the 4 left bytes. compIndex on the right most byte.
  93. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  94. internal static ulong Pack(uint netId, byte componentIndex) =>
  95. (ulong)netId << 32 | componentIndex;
  96. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  97. internal static void Unpack(ulong value, out uint netId, out byte componentIndex)
  98. {
  99. netId = (uint)(value >> 32);
  100. componentIndex = (byte)(value & 0xFF);
  101. }
  102. // helper function to find/get NetworkBehaviour to ulong (netId/compIndex)
  103. static T ULongToNetworkBehaviour(ulong value)
  104. {
  105. // unpack ulong to netId, componentIndex
  106. Unpack(value, out uint netId, out byte componentIndex);
  107. // find spawned NetworkIdentity by netId
  108. NetworkIdentity identity = Utils.GetSpawnedInServerOrClient(netId);
  109. // get the nth component
  110. return identity != null ? (T)identity.NetworkBehaviours[componentIndex] : null;
  111. }
  112. static ulong NetworkBehaviourToULong(T value)
  113. {
  114. // pack netId, componentIndex to ulong
  115. return value != null ? Pack(value.netId, (byte)value.ComponentIndex) : 0;
  116. }
  117. // Serialize should only write 4+1 bytes, not 8 bytes ulong
  118. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  119. public override void OnSerializeAll(NetworkWriter writer)
  120. {
  121. Unpack(base.Value, out uint netId, out byte componentIndex);
  122. writer.WriteUInt(netId);
  123. writer.WriteByte(componentIndex);
  124. }
  125. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  126. public override void OnSerializeDelta(NetworkWriter writer) =>
  127. OnSerializeAll(writer);
  128. // Deserialize should only write 4+1 bytes, not 8 bytes ulong
  129. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  130. public override void OnDeserializeAll(NetworkReader reader)
  131. {
  132. uint netId = reader.ReadUInt();
  133. byte componentIndex = reader.ReadByte();
  134. base.Value = Pack(netId, componentIndex);
  135. }
  136. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  137. public override void OnDeserializeDelta(NetworkReader reader) =>
  138. OnDeserializeAll(reader);
  139. }
  140. }