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