123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- // persistent GameObject SyncField which stores .netId internally.
- // this is necessary for cases like a player's target.
- // the target might run in and out of visibility range and become 'null'.
- // but the 'netId' remains and will always point to the monster if around.
- //
- // NOTE that SyncFieldNetworkIdentity is faster (no .gameObject/GetComponent<>)!
- //
- // original Weaver code with netId workaround:
- /*
- // USER:
- [SyncVar(hook = "OnTargetChanged")]
- public GameObject target;
- // WEAVER:
- private uint ___targetNetId;
- public GameObject Networktarget
- {
- get
- {
- return GetSyncVarGameObject(___targetNetId, ref target);
- }
- [param: In]
- set
- {
- if (!NetworkBehaviour.SyncVarGameObjectEqual(value, ___targetNetId))
- {
- GameObject networktarget = Networktarget;
- SetSyncVarGameObject(value, ref target, 1uL, ref ___targetNetId);
- if (NetworkServer.localClientActive && !GetSyncVarHookGuard(1uL))
- {
- SetSyncVarHookGuard(1uL, value: true);
- OnTargetChanged(networktarget, value);
- SetSyncVarHookGuard(1uL, value: false);
- }
- }
- }
- }
- private void OnTargetChanged(GameObject old, GameObject value)
- {
- }
- */
- using System;
- using System.Runtime.CompilerServices;
- using UnityEngine;
- namespace Mirror
- {
- // SyncField<GameObject> only stores an uint netId.
- // while providing .spawned lookup for convenience.
- // NOTE: server always knows all spawned. consider caching the field again.
- public class SyncVarGameObject : SyncVar<uint>
- {
- // .spawned lookup from netId overwrites base uint .Value
- public new GameObject Value
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => GetGameObject(base.Value);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set => base.Value = GetNetId(value);
- }
- // OnChanged Callback is for <uint, uint>.
- // Let's also have one for <GameObject, GameObject>
- public new event Action<GameObject, GameObject> Callback;
- // overwrite CallCallback to use the GameObject version instead
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected override void InvokeCallback(uint oldValue, uint newValue) =>
- Callback?.Invoke(GetGameObject(oldValue), GetGameObject(newValue));
- // ctor
- // 'value = null' so we can do:
- // SyncVarGameObject = new SyncVarGameObject()
- // instead of
- // SyncVarGameObject = new SyncVarGameObject(null);
- public SyncVarGameObject(GameObject value = null)
- : base(GetNetId(value)) {}
- // helper function to get netId from GameObject (if any)
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static uint GetNetId(GameObject go)
- {
- if (go != null)
- {
- NetworkIdentity identity = go.GetComponent<NetworkIdentity>();
- return identity != null ? identity.netId : 0;
- }
- return 0;
- }
- // helper function to get GameObject from netId (if spawned)
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static GameObject GetGameObject(uint netId)
- {
- NetworkIdentity spawned = Utils.GetSpawnedInServerOrClient(netId);
- return spawned != null ? spawned.gameObject : null;
- }
- // implicit conversion: GameObject value = SyncFieldGameObject
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static implicit operator GameObject(SyncVarGameObject field) => field.Value;
- // implicit conversion: SyncFieldGameObject = value
- // even if SyncField is readonly, it's still useful: SyncFieldGameObject = target;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static implicit operator SyncVarGameObject(GameObject value) => new SyncVarGameObject(value);
- // == operator for comparisons like Player.target==monster
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(SyncVarGameObject a, SyncVarGameObject b) =>
- a.Value == b.Value;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(SyncVarGameObject a, SyncVarGameObject b) => !(a == b);
- // NOTE: overloading all == operators blocks '== null' checks with an
- // "ambiguous invocation" error. that's good. this way user code like
- // "player.target == null" won't compile instead of silently failing!
- // == operator for comparisons like Player.target==monster
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(SyncVarGameObject a, GameObject b) =>
- a.Value == b;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(SyncVarGameObject a, GameObject b) => !(a == b);
- // == operator for comparisons like Player.target==monster
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(GameObject a, SyncVarGameObject b) =>
- a == b.Value;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(GameObject a, SyncVarGameObject b) => !(a == b);
- // if we overwrite == operators, we also need to overwrite .Equals.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override bool Equals(object obj) => obj is SyncVarGameObject value && this == value;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override int GetHashCode() => Value.GetHashCode();
- }
- }
|