SyncVarGameObject.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // persistent GameObject SyncField which stores .netId internally.
  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. //
  6. // NOTE that SyncFieldNetworkIdentity is faster (no .gameObject/GetComponent<>)!
  7. //
  8. // original Weaver code with netId workaround:
  9. /*
  10. // USER:
  11. [SyncVar(hook = "OnTargetChanged")]
  12. public GameObject target;
  13. // WEAVER:
  14. private uint ___targetNetId;
  15. public GameObject Networktarget
  16. {
  17. get
  18. {
  19. return GetSyncVarGameObject(___targetNetId, ref target);
  20. }
  21. [param: In]
  22. set
  23. {
  24. if (!NetworkBehaviour.SyncVarGameObjectEqual(value, ___targetNetId))
  25. {
  26. GameObject networktarget = Networktarget;
  27. SetSyncVarGameObject(value, ref target, 1uL, ref ___targetNetId);
  28. if (NetworkServer.localClientActive && !GetSyncVarHookGuard(1uL))
  29. {
  30. SetSyncVarHookGuard(1uL, value: true);
  31. OnTargetChanged(networktarget, value);
  32. SetSyncVarHookGuard(1uL, value: false);
  33. }
  34. }
  35. }
  36. }
  37. private void OnTargetChanged(GameObject old, GameObject value)
  38. {
  39. }
  40. */
  41. using System;
  42. using System.Runtime.CompilerServices;
  43. using UnityEngine;
  44. namespace Mirror
  45. {
  46. // SyncField<GameObject> only stores an uint netId.
  47. // while providing .spawned lookup for convenience.
  48. // NOTE: server always knows all spawned. consider caching the field again.
  49. public class SyncVarGameObject : SyncVar<uint>
  50. {
  51. // .spawned lookup from netId overwrites base uint .Value
  52. public new GameObject Value
  53. {
  54. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  55. get => GetGameObject(base.Value);
  56. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  57. set => base.Value = GetNetId(value);
  58. }
  59. // OnChanged Callback is for <uint, uint>.
  60. // Let's also have one for <GameObject, GameObject>
  61. public new event Action<GameObject, GameObject> Callback;
  62. // overwrite CallCallback to use the GameObject version instead
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. protected override void InvokeCallback(uint oldValue, uint newValue) =>
  65. Callback?.Invoke(GetGameObject(oldValue), GetGameObject(newValue));
  66. // ctor
  67. // 'value = null' so we can do:
  68. // SyncVarGameObject = new SyncVarGameObject()
  69. // instead of
  70. // SyncVarGameObject = new SyncVarGameObject(null);
  71. public SyncVarGameObject(GameObject value = null)
  72. : base(GetNetId(value)) {}
  73. // helper function to get netId from GameObject (if any)
  74. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  75. static uint GetNetId(GameObject go)
  76. {
  77. if (go != null)
  78. {
  79. NetworkIdentity identity = go.GetComponent<NetworkIdentity>();
  80. return identity != null ? identity.netId : 0;
  81. }
  82. return 0;
  83. }
  84. // helper function to get GameObject from netId (if spawned)
  85. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  86. static GameObject GetGameObject(uint netId)
  87. {
  88. NetworkIdentity spawned = Utils.GetSpawnedInServerOrClient(netId);
  89. return spawned != null ? spawned.gameObject : null;
  90. }
  91. // implicit conversion: GameObject value = SyncFieldGameObject
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. public static implicit operator GameObject(SyncVarGameObject field) => field.Value;
  94. // implicit conversion: SyncFieldGameObject = value
  95. // even if SyncField is readonly, it's still useful: SyncFieldGameObject = target;
  96. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  97. public static implicit operator SyncVarGameObject(GameObject value) => new SyncVarGameObject(value);
  98. // == operator for comparisons like Player.target==monster
  99. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  100. public static bool operator ==(SyncVarGameObject a, SyncVarGameObject b) =>
  101. a.Value == b.Value;
  102. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  103. public static bool operator !=(SyncVarGameObject a, SyncVarGameObject b) => !(a == b);
  104. // NOTE: overloading all == operators blocks '== null' checks with an
  105. // "ambiguous invocation" error. that's good. this way user code like
  106. // "player.target == null" won't compile instead of silently failing!
  107. // == operator for comparisons like Player.target==monster
  108. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  109. public static bool operator ==(SyncVarGameObject a, GameObject b) =>
  110. a.Value == b;
  111. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  112. public static bool operator !=(SyncVarGameObject a, GameObject b) => !(a == b);
  113. // == operator for comparisons like Player.target==monster
  114. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  115. public static bool operator ==(GameObject a, SyncVarGameObject b) =>
  116. a == b.Value;
  117. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  118. public static bool operator !=(GameObject a, SyncVarGameObject b) => !(a == b);
  119. // if we overwrite == operators, we also need to overwrite .Equals.
  120. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  121. public override bool Equals(object obj) => obj is SyncVarGameObject value && this == value;
  122. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  123. public override int GetHashCode() => Value.GetHashCode();
  124. }
  125. }