2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Runtime.CompilerServices;
9 public enum SyncMode { Observers, Owner }
12 [AddComponentMenu(
"")]
13 [RequireComponent(typeof(NetworkIdentity))]
14 [HelpURL(
"https://mirror-networking.gitbook.io/docs/guides/networkbehaviour")]
19 [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")]
20 [HideInInspector]
public SyncMode
syncMode = SyncMode.Observers;
25 [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.)")]
28 internal double lastSyncTime;
61 protected readonly List<SyncObject> syncObjects =
new List<SyncObject>();
64 internal bool HasSyncObjects() => syncObjects.Count > 0;
88 protected ulong syncVarDirtyBits {
get;
private set; }
92 internal ulong syncObjectDirtyBits;
99 ulong syncVarHookGuard;
102 protected bool GetSyncVarHookGuard(ulong dirtyBit) =>
103 (syncVarHookGuard & dirtyBit) != 0UL;
106 protected void SetSyncVarHookGuard(ulong dirtyBit,
bool value)
110 syncVarHookGuard |= dirtyBit;
113 syncVarHookGuard &= ~dirtyBit;
120 syncVarDirtyBits |= dirtyBit;
124 public bool IsDirty()
129 return (syncVarDirtyBits | syncObjectDirtyBits) != 0UL;
140 syncVarDirtyBits = 0L;
141 syncObjectDirtyBits = 0L;
145 for (
int i = 0; i < syncObjects.Count; ++i)
147 syncObjects[i].ClearChanges();
154 protected void InitSyncObject(
SyncObject syncObject)
156 if (syncObject ==
null)
158 Debug.LogError(
"Uninitialized SyncObject. Manually call the constructor on your SyncList, SyncSet, SyncDictionary or SyncField<T>");
163 int index = syncObjects.Count;
164 syncObjects.Add(syncObject);
167 ulong nthBit = 1UL << index;
168 syncObject.OnDirty = () => syncObjectDirtyBits |= nthBit;
179 protected void SendCommandInternal(
string functionFullName, NetworkWriter writer,
int channelId,
bool requiresAuthority =
true)
184 if (!NetworkClient.active)
186 Debug.LogError($
"Command Function {functionFullName} called on {name} without an active client.", gameObject);
192 if (!NetworkClient.ready)
197 if (channelId == Channels.Reliable)
198 Debug.LogWarning($
"Command Function {functionFullName} called on {name} while NetworkClient is not ready.\nThis may be ignored if client intentionally set NotReady.", gameObject);
205 Debug.LogWarning($
"Command Function {functionFullName} called on {name} without authority.", gameObject);
214 if (NetworkClient.connection ==
null)
216 Debug.LogError($
"Command Function {functionFullName} called on {name} with no client running.", gameObject);
221 CommandMessage message =
new CommandMessage
226 functionHash = (ushort)functionFullName.GetStableHashCode(),
228 payload = writer.ToArraySegment()
236 NetworkClient.connection.Send(message, channelId);
240 protected void SendRPCInternal(
string functionFullName, NetworkWriter writer,
int channelId,
bool includeOwner)
243 if (!NetworkServer.active)
245 Debug.LogError($
"RPC Function {functionFullName} called on Client.", gameObject);
252 Debug.LogWarning($
"ClientRpc {functionFullName} called on un-spawned object: {name}", gameObject);
257 RpcMessage message =
new RpcMessage
262 functionHash = (ushort)functionFullName.GetStableHashCode(),
264 payload = writer.ToArraySegment()
267 NetworkServer.SendToReadyObservers(
netIdentity, message, includeOwner, channelId);
271 protected void SendTargetRPCInternal(NetworkConnection conn,
string functionFullName, NetworkWriter writer,
int channelId)
273 if (!NetworkServer.active)
275 Debug.LogError($
"TargetRPC {functionFullName} called on {name} when server not active", gameObject);
281 Debug.LogWarning($
"TargetRpc {functionFullName} called on {name} but that object has not been spawned or has been unspawned", gameObject);
294 Debug.LogError($
"TargetRPC {functionFullName} was given a null connection, make sure the object {name} has an owner or you pass in the target connection", gameObject);
298 if (!(conn is NetworkConnectionToClient))
300 Debug.LogError($
"TargetRPC {functionFullName} called on {name} requires a NetworkConnectionToClient but was given {conn.GetType().Name}", gameObject);
305 RpcMessage message =
new RpcMessage
310 functionHash = (ushort)functionFullName.GetStableHashCode(),
312 payload = writer.ToArraySegment()
315 conn.Send(message, channelId);
344 [MethodImpl(MethodImplOptions.AggressiveInlining)]
345 public void GeneratedSyncVarSetter<T>(T value, ref T field, ulong dirtyBit, Action<T, T> OnChanged)
347 if (!SyncVarEqual(value, ref field))
350 SetSyncVar(value, ref field, dirtyBit);
353 if (OnChanged !=
null)
359 if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit))
361 SetSyncVarHookGuard(dirtyBit,
true);
362 OnChanged(oldValue, value);
363 SetSyncVarHookGuard(dirtyBit,
false);
371 [MethodImpl(MethodImplOptions.AggressiveInlining)]
372 public void GeneratedSyncVarSetter_GameObject(GameObject value, ref GameObject field, ulong dirtyBit, Action<GameObject, GameObject> OnChanged, ref uint netIdField)
374 if (!SyncVarGameObjectEqual(value, netIdField))
376 GameObject oldValue = field;
377 SetSyncVarGameObject(value, ref field, dirtyBit, ref netIdField);
380 if (OnChanged !=
null)
386 if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit))
388 SetSyncVarHookGuard(dirtyBit,
true);
389 OnChanged(oldValue, value);
390 SetSyncVarHookGuard(dirtyBit,
false);
398 [MethodImpl(MethodImplOptions.AggressiveInlining)]
399 public void GeneratedSyncVarSetter_NetworkIdentity(NetworkIdentity value, ref NetworkIdentity field, ulong dirtyBit, Action<NetworkIdentity, NetworkIdentity> OnChanged, ref uint netIdField)
401 if (!SyncVarNetworkIdentityEqual(value, netIdField))
403 NetworkIdentity oldValue = field;
404 SetSyncVarNetworkIdentity(value, ref field, dirtyBit, ref netIdField);
407 if (OnChanged !=
null)
413 if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit))
415 SetSyncVarHookGuard(dirtyBit,
true);
416 OnChanged(oldValue, value);
417 SetSyncVarHookGuard(dirtyBit,
false);
425 [MethodImpl(MethodImplOptions.AggressiveInlining)]
426 public void GeneratedSyncVarSetter_NetworkBehaviour<T>(T value, ref T field, ulong dirtyBit, Action<T, T> OnChanged, ref NetworkBehaviourSyncVar netIdField)
427 where T : NetworkBehaviour
429 if (!SyncVarNetworkBehaviourEqual(value, netIdField))
432 SetSyncVarNetworkBehaviour(value, ref field, dirtyBit, ref netIdField);
435 if (OnChanged !=
null)
441 if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit))
443 SetSyncVarHookGuard(dirtyBit,
true);
444 OnChanged(oldValue, value);
445 SetSyncVarHookGuard(dirtyBit,
false);
454 [EditorBrowsable(EditorBrowsableState.Never)]
455 public static bool SyncVarGameObjectEqual(GameObject newGameObject, uint netIdField)
458 if (newGameObject !=
null)
460 NetworkIdentity identity = newGameObject.GetComponent<NetworkIdentity>();
461 if (identity !=
null)
463 newNetId = identity.netId;
466 Debug.LogWarning($
"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?");
471 return newNetId == netIdField;
476 protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, ulong dirtyBit, ref uint netIdField)
478 if (GetSyncVarHookGuard(dirtyBit))
482 if (newGameObject !=
null)
484 NetworkIdentity identity = newGameObject.GetComponent<NetworkIdentity>();
485 if (identity !=
null)
487 newNetId = identity.netId;
490 Debug.LogWarning($
"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?");
498 gameObjectField = newGameObject;
499 netIdField = newNetId;
504 protected GameObject GetSyncVarGameObject(uint
netId, ref GameObject gameObjectField)
509 return gameObjectField;
514 if (NetworkClient.spawned.TryGetValue(
netId, out NetworkIdentity identity) && identity !=
null)
515 return gameObjectField = identity.gameObject;
522 [EditorBrowsable(EditorBrowsableState.Never)]
523 public static bool SyncVarNetworkIdentityEqual(NetworkIdentity newIdentity, uint netIdField)
526 if (newIdentity !=
null)
528 newNetId = newIdentity.netId;
531 Debug.LogWarning($
"SetSyncVarNetworkIdentity NetworkIdentity {newIdentity} has a zero netId. Maybe it is not spawned yet?");
536 return newNetId == netIdField;
583 [MethodImpl(MethodImplOptions.AggressiveInlining)]
584 public void GeneratedSyncVarDeserialize<T>(ref T field, Action<T, T> OnChanged, T value)
590 if (OnChanged !=
null && !SyncVarEqual(previous, ref field))
592 OnChanged(previous, field);
641 [MethodImpl(MethodImplOptions.AggressiveInlining)]
642 public void GeneratedSyncVarDeserialize_GameObject(ref GameObject field, Action<GameObject, GameObject> OnChanged, NetworkReader reader, ref uint netIdField)
644 uint previousNetId = netIdField;
645 GameObject previousGameObject = field;
646 netIdField = reader.ReadUInt();
649 field = GetSyncVarGameObject(netIdField, ref field);
652 if (OnChanged !=
null && !SyncVarEqual(previousNetId, ref netIdField))
654 OnChanged(previousGameObject, field);
704 [MethodImpl(MethodImplOptions.AggressiveInlining)]
705 public void GeneratedSyncVarDeserialize_NetworkIdentity(ref NetworkIdentity field, Action<NetworkIdentity, NetworkIdentity> OnChanged, NetworkReader reader, ref uint netIdField)
707 uint previousNetId = netIdField;
708 NetworkIdentity previousIdentity = field;
709 netIdField = reader.ReadUInt();
712 field = GetSyncVarNetworkIdentity(netIdField, ref field);
715 if (OnChanged !=
null && !SyncVarEqual(previousNetId, ref netIdField))
717 OnChanged(previousIdentity, field);
768 [MethodImpl(MethodImplOptions.AggressiveInlining)]
769 public void GeneratedSyncVarDeserialize_NetworkBehaviour<T>(ref T field, Action<T, T> OnChanged, NetworkReader reader, ref NetworkBehaviourSyncVar netIdField)
770 where T : NetworkBehaviour
772 NetworkBehaviourSyncVar previousNetId = netIdField;
773 T previousBehaviour = field;
774 netIdField = reader.ReadNetworkBehaviourSyncVar();
777 field = GetSyncVarNetworkBehaviour(netIdField, ref field);
780 if (OnChanged !=
null && !SyncVarEqual(previousNetId, ref netIdField))
782 OnChanged(previousBehaviour, field);
788 protected void SetSyncVarNetworkIdentity(NetworkIdentity newIdentity, ref NetworkIdentity identityField, ulong dirtyBit, ref uint netIdField)
790 if (GetSyncVarHookGuard(dirtyBit))
794 if (newIdentity !=
null)
796 newNetId = newIdentity.netId;
799 Debug.LogWarning($
"SetSyncVarNetworkIdentity NetworkIdentity {newIdentity} has a zero netId. Maybe it is not spawned yet?");
805 netIdField = newNetId;
807 identityField = newIdentity;
812 protected NetworkIdentity GetSyncVarNetworkIdentity(uint
netId, ref NetworkIdentity identityField)
817 return identityField;
822 NetworkClient.spawned.TryGetValue(
netId, out identityField);
823 return identityField;
826 protected static bool SyncVarNetworkBehaviourEqual<T>(T newBehaviour, NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour
829 int newComponentIndex = 0;
830 if (newBehaviour !=
null)
832 newNetId = newBehaviour.netId;
833 newComponentIndex = newBehaviour.ComponentIndex;
836 Debug.LogWarning($
"SetSyncVarNetworkIdentity NetworkIdentity {newBehaviour} has a zero netId. Maybe it is not spawned yet?");
841 return syncField.Equals(newNetId, newComponentIndex);
846 protected void SetSyncVarNetworkBehaviour<T>(T newBehaviour, ref T behaviourField, ulong dirtyBit, ref NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour
848 if (GetSyncVarHookGuard(dirtyBit))
852 int componentIndex = 0;
853 if (newBehaviour !=
null)
855 newNetId = newBehaviour.netId;
856 componentIndex = newBehaviour.ComponentIndex;
859 Debug.LogWarning($
"{nameof(SetSyncVarNetworkBehaviour)} NetworkIdentity {newBehaviour} has a zero netId. Maybe it is not spawned yet?");
863 syncField =
new NetworkBehaviourSyncVar(newNetId, componentIndex);
868 behaviourField = newBehaviour;
875 protected T GetSyncVarNetworkBehaviour<T>(NetworkBehaviourSyncVar syncNetBehaviour, ref T behaviourField) where T : NetworkBehaviour
880 return behaviourField;
885 if (!NetworkClient.spawned.TryGetValue(syncNetBehaviour.netId, out NetworkIdentity identity))
890 behaviourField = identity.NetworkBehaviours[syncNetBehaviour.componentIndex] as T;
891 return behaviourField;
899 public byte componentIndex;
904 this.componentIndex = (byte)componentIndex;
909 return other.netId == netId && other.componentIndex == componentIndex;
912 public bool Equals(uint netId,
int componentIndex)
914 return this.netId == netId && this.componentIndex == componentIndex;
917 public override string ToString()
919 return $
"[netId:{netId} compIndex:{componentIndex}]";
923 protected static bool SyncVarEqual<T>(T value, ref T fieldValue)
929 return EqualityComparer<T>.Default.Equals(value, fieldValue);
933 protected void SetSyncVar<T>(T value, ref T fieldValue, ulong dirtyBit)
950 bool objectWritten = initialState ? SerializeObjectsAll(writer) : SerializeObjectsDelta(writer);
951 bool syncVarWritten = SerializeSyncVars(writer, initialState);
952 return objectWritten || syncVarWritten;
960 DeSerializeObjectsAll(reader);
964 DeSerializeObjectsDelta(reader);
967 DeserializeSyncVars(reader, initialState);
971 protected virtual bool SerializeSyncVars(
NetworkWriter writer,
bool initialState)
985 protected virtual void DeserializeSyncVars(NetworkReader reader,
bool initialState)
996 public bool SerializeObjectsAll(NetworkWriter writer)
999 for (
int i = 0; i < syncObjects.Count; i++)
1001 SyncObject syncObject = syncObjects[i];
1008 public bool SerializeObjectsDelta(NetworkWriter writer)
1012 writer.WriteULong(syncObjectDirtyBits);
1014 for (
int i = 0; i < syncObjects.Count; i++)
1017 SyncObject syncObject = syncObjects[i];
1018 if ((syncObjectDirtyBits & (1UL << i)) != 0)
1027 internal void DeSerializeObjectsAll(NetworkReader reader)
1029 for (
int i = 0; i < syncObjects.Count; i++)
1031 SyncObject syncObject = syncObjects[i];
1032 syncObject.OnDeserializeAll(reader);
1036 internal void DeSerializeObjectsDelta(NetworkReader reader)
1038 ulong dirty = reader.ReadULong();
1039 for (
int i = 0; i < syncObjects.Count; i++)
1042 SyncObject syncObject = syncObjects[i];
1043 if ((dirty & (1UL << i)) != 0)
1045 syncObject.OnDeserializeDelta(reader);
1050 internal void ResetSyncObjects()
1052 foreach (SyncObject syncObject
in syncObjects)
Base class for networked components.
void ClearAllDirtyBits()
Clears all the dirty bits that were set by SetDirtyBits()
bool isLocalPlayer
True if this object is the the client's own local player.
void SetSyncVarDirtyBit(ulong dirtyBit)
Set as dirty so that it's synced to clients again.
virtual void OnStartAuthority()
Like Start(), but only called for objects the client has authority over.
NetworkIdentity netIdentity
Returns the NetworkIdentity of this object
virtual void OnDeserialize(NetworkReader reader, bool initialState)
Override to do custom deserialization (instead of SyncVars/SyncLists). Use OnSerialize too.
virtual void OnStopServer()
Stop event, only called on server and host.
int ComponentIndex
Returns the index of the component on this object
virtual void OnStartClient()
Like Start(), but only called on client and host.
bool isServer
True if this object is on the server and has been spawned.
virtual void OnStartServer()
Like Start(), but only called on server and host.
virtual void OnStartLocalPlayer()
Like Start(), but only called on client and host for the local player object.
bool isServerOnly
True if this object is on the server-only, not host.
float syncInterval
sync interval for OnSerialize (in seconds)
NetworkConnection connectionToServer
Client's network connection to the server. This is only valid for player objects on the client.
bool isClient
True if this object is on the client and has been spawned by the server.
NetworkConnectionToClient connectionToClient
Server's network connection to the client. This is only valid for player objects on the server.
bool hasAuthority
True on client if that component has been assigned to the client. E.g. player, pets,...
bool isClientOnly
True if this object is on the client-only, not host.
SyncMode syncMode
sync mode for OnSerialize
virtual void OnStopClient()
Stop event, only called on client and host.
virtual void OnStopLocalPlayer()
Stop event, but only called on client and host for the local player object.
uint netId
The unique network Id of this object (unique at runtime).
virtual bool OnSerialize(NetworkWriter writer, bool initialState)
Override to do custom serialization (instead of SyncVars/SyncLists). Use OnDeserialize too.
virtual void OnStopAuthority()
Stop event, only called for objects the client has authority over.
Base NetworkConnection class for server-to-client and client-to-server connection.
NetworkIdentity identifies objects across the network.
bool isClient
Returns true if running as a client and this object was spawned by a server.
bool hasAuthority
True on client if that component has been assigned to the client. E.g. player, pets,...
uint netId
The unique network Id of this object (unique at runtime).
bool isServer
Returns true if NetworkServer.active and server is not stopped.
bool isLocalPlayer
Return true if this object represents the player on the local machine.
Dictionary< int, NetworkConnectionToClient > observers
The set of network connections (players) that can see this object.
bool isServerOnly
True if this object only exists on the server
NetworkConnectionToClient connectionToClient
Server's network connection to the client. This is only valid for client-owned objects (including the...
bool isClientOnly
True if this object exists on a client that is not also acting as a server.
NetworkConnection connectionToServer
Client's network connection to the server. This is only valid for player objects on the client.
Network Reader for most simple types like floats, ints, buffers, structs, etc. Use NetworkReaderPool....
Synchronizes server time to clients.
Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool....
SyncObjects sync state between server and client. E.g. SyncLists.
abstract void OnSerializeAll(NetworkWriter writer)
Write a full copy of the object
abstract void OnSerializeDelta(NetworkWriter writer)
Write the changes made to the object since last sync