#if UNITY_EDITOR || DEVELOPMENT_BUILD #define DEVELOPMENT #endif using FishNet.CodeGenerating; using FishNet.Connection; using FishNet.Documenting; using FishNet.Managing; using FishNet.Object; using FishNet.Object.Prediction; using FishNet.Serializing.Helping; using FishNet.Transporting; using FishNet.Utility; using FishNet.Utility.Performance; using GameKit.Dependencies.Utilities; using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Text; using UnityEngine; [assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] //Required for internal tests. [assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)] namespace FishNet.Serializing { /// /// Reads data from a buffer. /// public partial class Reader { #region Types. public enum DataSource { Unset = 0, Server = 1, Client = 2, } #endregion #region Public. /// /// Which part of the network the data came from. /// public DataSource Source = DataSource.Unset; /// /// Capacity of the buffer. /// public int Capacity => _buffer.Length; /// /// NetworkManager for this reader. Used to lookup objects. /// public NetworkManager NetworkManager; /// /// Offset within the buffer when the reader was created. /// public int Offset { get; private set; } /// /// Position for the next read. /// public int Position; /// /// Total number of bytes available within the buffer. /// public int Length { get; private set; } /// /// Bytes remaining to be read. This value is Length - Position. /// public int Remaining => ((Length + Offset) - Position); #endregion #region Internal. /// /// NetworkConnection that this data came from. /// Value may not always be set. /// public NetworkConnection NetworkConnection { get; private set; } #if DEVELOPMENT /// /// Last NetworkObject parsed. /// public static NetworkObject LastNetworkObject { get; private set; } /// /// Last NetworkBehaviour parsed. /// public static NetworkBehaviour LastNetworkBehaviour { get; private set; } #endif #endregion #region Private. /// /// Data being read. /// private byte[] _buffer; /// /// Used to convert bytes to a string. /// private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); /// /// Used to convert bytes to a GUID. /// private static readonly byte[] _guidBuffer = new byte[16]; #endregion public Reader() { } public Reader(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) { Initialize(bytes, networkManager, networkConnection, source); } public Reader(ArraySegment segment, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) { Initialize(segment, networkManager, networkConnection, source); } /// /// Outputs reader to string. /// /// public override string ToString() => ToString(0, Length); /// /// Outputs reader to string starting at an index. /// /// public string ToString(int offset, int length) { return $"Position: {Position:0000}, Length: {Length:0000}, Buffer: {BitConverter.ToString(_buffer, offset, length)}."; } /// /// Outputs reader to string. /// /// public string RemainingToString() { string buffer = (Remaining > 0) ? BitConverter.ToString(_buffer, Position, Remaining) : "null"; return $"Remaining: {Remaining}, Length: {Length}, Buffer: {buffer}."; } /// /// Returns remaining data as an ArraySegment. /// /// public ArraySegment GetRemainingData() { if (Remaining == 0) return default; else return new(_buffer, Position, Remaining); } /// /// Initializes this reader with data. /// public void Initialize(ArraySegment segment, NetworkManager networkManager, DataSource source = DataSource.Unset) { Initialize(segment, networkManager, null, source); } /// /// Initializes this reader with data. /// public void Initialize(ArraySegment segment, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) { _buffer = segment.Array; if (_buffer == null) _buffer = new byte[0]; Position = segment.Offset; Offset = segment.Offset; Length = segment.Count; NetworkManager = networkManager; NetworkConnection = networkConnection; Source = source; } /// /// Initializes this reader with data. /// public void Initialize(byte[] bytes, NetworkManager networkManager, DataSource source = DataSource.Unset) { Initialize(new ArraySegment(bytes), networkManager, null, source); } /// /// Initializes this reader with data. /// public void Initialize(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) { Initialize(new ArraySegment(bytes), networkManager, networkConnection, source); } /// /// Reads length. This method is used to make debugging easier. /// internal int ReadLength() { return ReadInt32(); } /// /// Reads a packetId. /// internal PacketId ReadPacketId() { return (PacketId)ReadUInt16Unpacked(); } /// /// Returns a ushort without advancing the reader. /// /// internal PacketId PeekPacketId() { int currentPosition = Position; PacketId result = ReadPacketId(); Position = currentPosition; return result; } /// /// Returns the next byte to be read. /// /// internal byte PeekUInt8() { return _buffer[Position]; } /// /// Skips a number of bytes in the reader. /// /// Number of bytes to skip. public void Skip(int value) { if (value < 1 || Remaining < value) return; Position += value; } /// /// Clears remaining bytes to be read. /// public void Clear() { if (Remaining > 0) Skip(Remaining); } /// /// Returns the buffer as an ArraySegment. /// /// public ArraySegment GetArraySegmentBuffer() { return new(_buffer, Offset, Length); } [Obsolete("Use GetBuffer.")] //Remove V5 public byte[] GetByteBuffer() => GetBuffer(); /// /// Returns the buffer as bytes. This does not trim excessive bytes. /// /// public byte[] GetBuffer() { return _buffer; } [Obsolete("Use GetBufferAllocated().")] //Remove V5 public byte[] GetByteBufferAllocated() => GetBufferAllocated(); /// /// Returns the buffer as bytes and allocates into a new array. /// /// [Obsolete("Use GetBufferAllocated().")] //Remove V5 public byte[] GetBufferAllocated() { byte[] result = new byte[Length]; Buffer.BlockCopy(_buffer, Offset, result, 0, Length); return result; } /// /// BlockCopies data from the reader to target and advances reader. /// /// /// /// public void BlockCopy(ref byte[] target, int targetOffset, int count) { Buffer.BlockCopy(_buffer, Position, target, targetOffset, count); Position += count; } [Obsolete("Use ReadUInt8Unpacked.")] //Remove in V5. public byte ReadByte() => ReadUInt8Unpacked(); /// /// Reads a byte. /// /// [DefaultReader] public byte ReadUInt8Unpacked() { byte r = _buffer[Position]; Position += 1; return r; } [Obsolete("Use ReadUInt8ArrayAllocated.")] public byte[] ReadBytesAllocated(int count) => ReadUInt8ArrayAllocated(count); [Obsolete("Use ReadUInt8Array.")] public void ReadBytes(ref byte[] buffer, int count) => ReadUInt8Array(ref buffer, count); /// /// Read bytes from position into target. /// /// Buffer to read bytes into. /// Number of bytes to read. public void ReadUInt8Array(ref byte[] buffer, int count) { if (buffer == null) NetworkManager.LogError($"Buffer cannot be null."); else if (count > buffer.Length) NetworkManager.LogError($"Count of {count} exceeds target length of {buffer.Length}."); else BlockCopy(ref buffer, 0, count); } /// /// Creates an ArraySegment by reading a number of bytes from position. /// /// /// public ArraySegment ReadArraySegment(int count) { if (count < 0) { NetworkManager.Log($"ArraySegment count cannot be less than 0."); //Purge renaming and return default. Position += Remaining; return default; } ArraySegment result = new(_buffer, Position, count); Position += count; return result; } [Obsolete("Use ReadInt8Unpacked.")] //Remove in V5. public sbyte ReadSByte() => ReadInt8Unpacked(); /// /// Reads a sbyte. /// /// [DefaultReader] public sbyte ReadInt8Unpacked() => (sbyte)ReadUInt8Unpacked(); /// /// Reads a char. /// /// [DefaultReader] public char ReadChar() => (char)ReadUInt16(); /// /// Reads a boolean. /// /// [DefaultReader] public bool ReadBoolean() { byte result = ReadUInt8Unpacked(); return (result == 1) ? true : false; } /// /// Reads an int16. /// /// public ushort ReadUInt16Unpacked() { ushort result = 0; result |= _buffer[Position++]; result |= (ushort)(_buffer[Position++] << 8); return result; } /// /// Reads an int16. /// /// //todo: should be using ReadPackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. [DefaultReader] public ushort ReadUInt16() => ReadUInt16Unpacked(); /// /// Reads a uint16. /// /// //todo: should be using ReadPackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. public short ReadInt16Unpacked() => (short)ReadUInt16Unpacked(); /// /// Reads a uint16. /// /// //todo: should be using ReadPackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. [DefaultReader] public short ReadInt16() => (short)ReadUInt16Unpacked(); /// /// Reads an int32. /// /// public uint ReadUInt32Unpacked() { uint result = 0; result |= _buffer[Position++]; result |= (uint)_buffer[Position++] << 8; result |= (uint)_buffer[Position++] << 16; result |= (uint)_buffer[Position++] << 24; return result; } /// /// Reads an int32. /// /// [DefaultReader] public uint ReadUInt32() => (uint)ReadUnsignedPackedWhole(); /// /// Reads a uint32. /// /// public int ReadInt32Unpacked() => (int)ReadUInt32Unpacked(); /// /// Reads a uint32. /// /// [DefaultReader] public int ReadInt32() => (int)ReadSignedPackedWhole(); /// /// Reads a uint64. /// /// public long ReadInt64Unpacked() => (long)ReadUInt64Unpacked(); /// /// Reads a uint64. /// /// [DefaultReader] public long ReadInt64() => (long)ReadSignedPackedWhole(); /// /// Reads an int64. /// /// public ulong ReadUInt64Unpacked() { ulong result = 0; result |= _buffer[Position++]; result |= (ulong)_buffer[Position++] << 8; result |= (ulong)_buffer[Position++] << 16; result |= (ulong)_buffer[Position++] << 24; result |= (ulong)_buffer[Position++] << 32; result |= (ulong)_buffer[Position++] << 40; result |= (ulong)_buffer[Position++] << 48; result |= (ulong)_buffer[Position++] << 56; return result; } /// /// Reads an int64. /// /// [DefaultReader] public ulong ReadUInt64() => ReadUnsignedPackedWhole(); /// /// Reads a single. /// /// public float ReadSingleUnpacked() { UIntFloat converter = new(); converter.UIntValue = ReadUInt32Unpacked(); return converter.FloatValue; } /// /// Reads a single. /// /// [DefaultReader] public float ReadSingle() => ReadSingleUnpacked(); /// /// Reads a double. /// /// public double ReadDoubleUnpacked() { UIntDouble converter = new(); converter.LongValue = ReadUInt64Unpacked(); return converter.DoubleValue; } /// /// Reads a double. /// /// [DefaultReader] public double ReadDouble() => ReadDoubleUnpacked(); /// /// Reads a decimal. /// /// public decimal ReadDecimalUnpacked() { UIntDecimal converter = new(); converter.LongValue1 = ReadUInt64Unpacked(); converter.LongValue2 = ReadUInt64Unpacked(); return converter.DecimalValue; } /// /// Reads a decimal. /// /// [DefaultReader] public decimal ReadDecimal() => ReadDecimalUnpacked(); [Obsolete("use ReadStringAllocated.")] public string ReadString() => ReadStringAllocated(); /// /// Reads a string. /// /// [DefaultReader] public string ReadStringAllocated() { int length = ReadInt32(); //Null string. if (length == Writer.UNSET_COLLECTION_SIZE_VALUE) return null; if (length == 0) return string.Empty; if (!CheckAllocationAttack(length)) return string.Empty; ArraySegment data = ReadArraySegment(length); return data.Array.ToString(data.Offset, data.Count); } [Obsolete("Use ReadUInt8ArrayAndSizeAllocated.")] public byte[] ReadBytesAndSizeAllocated() => ReadUInt8ArrayAndSizeAllocated(); /// /// Creates a byte array and reads bytes and size into it. /// /// [DefaultReader] public byte[] ReadUInt8ArrayAndSizeAllocated() { int size = ReadInt32(); if (size == Writer.UNSET_COLLECTION_SIZE_VALUE) return null; return ReadUInt8ArrayAllocated(size); } [Obsolete("Use ReadUInt8ArrayAndSize.")] public int ReadBytesAndSize(ref byte[] target) => ReadUInt8ArrayAndSize(ref target); /// /// Reads bytes and size and copies results into target. Returns UNSET if null was written. /// /// Bytes read. public int ReadUInt8ArrayAndSize(ref byte[] target) { int size = ReadInt32(); if (size > 0) ReadUInt8Array(ref target, size); return size; } /// /// Reads bytes and size and returns as an ArraySegment. /// /// [DefaultReader] public ArraySegment ReadArraySegmentAndSize() { int size = ReadInt32(); /* UNSET would be written for null. But since * ArraySegments cannot be null return default if * length is unset or 0. */ if (size == Writer.UNSET_COLLECTION_SIZE_VALUE) return default; return ReadArraySegment(size); } /// /// Reads a Vector2. /// /// public Vector2 ReadVector2Unpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked()); /// /// Reads a Vector2. /// /// [DefaultReader] public Vector2 ReadVector2() => ReadVector2Unpacked(); /// /// Reads a Vector3. /// /// public Vector3 ReadVector3Unpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); /// /// Reads a Vector3. /// /// [DefaultReader] public Vector3 ReadVector3() => ReadVector3Unpacked(); /// /// Reads a Vector4. /// /// public Vector4 ReadVector4Unpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); /// /// Reads a Vector4. /// /// [DefaultReader] public Vector4 ReadVector4() => ReadVector4Unpacked(); /// /// Reads a Vector2Int. /// /// public Vector2Int ReadVector2IntUnpacked() => new(ReadInt32Unpacked(), ReadInt32Unpacked()); /// /// Reads a Vector2Int. /// /// [DefaultReader] public Vector2Int ReadVector2Int() => new((int)ReadSignedPackedWhole(), (int)ReadSignedPackedWhole()); /// /// Reads a Vector3Int. /// /// public Vector3Int ReadVector3IntUnpacked() => new(ReadInt32Unpacked(), ReadInt32Unpacked(), ReadInt32Unpacked()); /// /// Reads a Vector3Int. /// /// [DefaultReader] public Vector3Int ReadVector3Int() => new((int)ReadSignedPackedWhole(), (int)ReadSignedPackedWhole(), (int)ReadSignedPackedWhole()); /// /// Reads a color. /// /// public Color ReadColorUnpacked() { float r = ReadSingleUnpacked(); float g = ReadSingleUnpacked(); float b = ReadSingleUnpacked(); float a = ReadSingleUnpacked(); return new(r, g, b, a); } /// /// Reads a color. /// /// [DefaultReader] public Color ReadColor() { float r = (float)(ReadUInt8Unpacked() / 100f); float g = (float)(ReadUInt8Unpacked() / 100f); float b = (float)(ReadUInt8Unpacked() / 100f); float a = (float)(ReadUInt8Unpacked() / 100f); return new(r, g, b, a); } /// /// Reads a Color32. /// /// [DefaultReader] public Color32 ReadColor32() => new(ReadUInt8Unpacked(), ReadUInt8Unpacked(), ReadUInt8Unpacked(), ReadUInt8Unpacked()); /// /// Reads a Quaternion. /// /// public Quaternion ReadQuaternionUnpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); /// /// Reads a Quaternion. /// /// public Quaternion ReadQuaternion64() { ulong result = ReadUInt64Unpacked(); return Quaternion64Compression.Decompress(result); } /// /// Reads a Quaternion. /// /// [DefaultReader] public Quaternion ReadQuaternion32() { return Quaternion32Compression.Decompress(this); } /// /// Reads a Quaternion. /// /// internal Quaternion ReadQuaternion(AutoPackType autoPackType) { switch (autoPackType) { case AutoPackType.Packed: return ReadQuaternion32(); case AutoPackType.PackedLess: return ReadQuaternion64(); default: return ReadQuaternionUnpacked(); } } /// /// Reads a Rect. /// /// public Rect ReadRectUnpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); /// /// Reads a Rect. /// /// [DefaultReader] public Rect ReadRect() => ReadRectUnpacked(); /// /// Plane. /// /// public Plane ReadPlaneUnpacked() => new(ReadVector3Unpacked(), ReadSingleUnpacked()); /// /// Plane. /// /// [DefaultReader] public Plane ReadPlane() => ReadPlaneUnpacked(); /// /// Reads a Ray. /// /// public Ray ReadRayUnpacked() { Vector3 position = ReadVector3Unpacked(); Vector3 direction = ReadVector3Unpacked(); return new(position, direction); } /// /// Reads a Ray. /// /// [DefaultReader] public Ray ReadRay() => ReadRayUnpacked(); /// /// Reads a Ray. /// /// public Ray2D ReadRay2DUnpacked() { Vector3 position = ReadVector2Unpacked(); Vector2 direction = ReadVector2Unpacked(); return new(position, direction); } /// /// Reads a Ray. /// /// [DefaultReader] public Ray2D ReadRay2D() => ReadRay2DUnpacked(); /// /// Reads a Matrix4x4. /// /// public Matrix4x4 ReadMatrix4x4Unpacked() { Matrix4x4 result = new() { m00 = ReadSingleUnpacked(), m01 = ReadSingleUnpacked(), m02 = ReadSingleUnpacked(), m03 = ReadSingleUnpacked(), m10 = ReadSingleUnpacked(), m11 = ReadSingleUnpacked(), m12 = ReadSingleUnpacked(), m13 = ReadSingleUnpacked(), m20 = ReadSingleUnpacked(), m21 = ReadSingleUnpacked(), m22 = ReadSingleUnpacked(), m23 = ReadSingleUnpacked(), m30 = ReadSingleUnpacked(), m31 = ReadSingleUnpacked(), m32 = ReadSingleUnpacked(), m33 = ReadSingleUnpacked() }; return result; } /// /// Reads a Matrix4x4. /// /// [DefaultReader] public Matrix4x4 ReadMatrix4x4() => ReadMatrix4x4Unpacked(); /// /// Creates a new byte array and reads bytes into it. /// /// /// public byte[] ReadUInt8ArrayAllocated(int count) { if (count < 0) { NetworkManager.Log($"Bytes count cannot be less than 0."); //Purge renaming and return default. Position += Remaining; return default; } byte[] bytes = new byte[count]; ReadUInt8Array(ref bytes, count); return bytes; } /// /// Reads a Guid. /// /// [DefaultReader] public System.Guid ReadGuid() { byte[] buffer = _guidBuffer; ReadUInt8Array(ref buffer, 16); return new(buffer); } /// /// Reads a tick without packing. /// public uint ReadTickUnpacked() => ReadUInt32Unpacked(); /// /// Reads a GameObject. /// /// [DefaultReader] public GameObject ReadGameObject() { byte writtenType = ReadUInt8Unpacked(); GameObject result; //Do nothing for 0, as it indicates null. if (writtenType == 0) { result = null; } //1 indicates a networkObject. else if (writtenType == 1) { NetworkObject nob = ReadNetworkObject(); result = (nob == null) ? null : nob.gameObject; } //2 indicates a networkBehaviour. else if (writtenType == 2) { NetworkBehaviour nb = ReadNetworkBehaviour(); result = (nb == null) ? null : nb.gameObject; } else { result = null; NetworkManager.LogError($"Unhandled ReadGameObject type of {writtenType}."); } return result; } /// /// Reads a Transform. /// /// [DefaultReader] public Transform ReadTransform() { NetworkObject nob = ReadNetworkObject(); return (nob == null) ? null : nob.transform; } /// /// Reads a NetworkObject. /// /// [DefaultReader] public NetworkObject ReadNetworkObject() => ReadNetworkObject(out _); /// /// Reads a NetworkObject. /// /// public NetworkObject ReadNetworkObject(bool logException) => ReadNetworkObject(out _, null, logException); /// /// Reads a NetworkObject. /// /// Objects which have been read to be spawned this tick, but may not have spawned yet. /// public NetworkObject ReadNetworkObject(out int objectOrPrefabId, HashSet readSpawningObjects = null, bool logException = true) { #if DEVELOPMENT LastNetworkBehaviour = null; #endif objectOrPrefabId = ReadNetworkObjectId(); bool isSpawned; /* UNSET indicates that the object * is null or no PrefabId is set. * PrefabIds are set in Awake within * the NetworkManager so that should * never happen so long as nob isn't null. */ if (objectOrPrefabId == NetworkObject.UNSET_OBJECTID_VALUE) return null; else isSpawned = ReadBoolean(); bool isServer = NetworkManager.ServerManager.Started; bool isClient = NetworkManager.ClientManager.Started; NetworkObject result; //Is spawned. if (isSpawned) { result = null; /* Try to get the object client side first if client * is running. When acting as a host generally the object * will be available in the server and client list * but there can be occasions where the server side * deinitializes the object, making it unavailable, while * it is still available in the client side. Since FishNet doesn't * use a fake host connection like some lesser solutions the client * has to always be treated as it's own entity. */ if (isClient) NetworkManager.ClientManager.Objects.Spawned.TryGetValueIL2CPP(objectOrPrefabId, out result); //If not found on client and server is running then try server. if (result == null && isServer) NetworkManager.ServerManager.Objects.Spawned.TryGetValueIL2CPP(objectOrPrefabId, out result); if (result == null && !isServer) { if (logException && (readSpawningObjects == null || !readSpawningObjects.Contains(objectOrPrefabId))) NetworkManager.LogWarning($"Spawned NetworkObject was expected to exist but does not for Id {objectOrPrefabId}. This may occur if you sent a NetworkObject reference which does not exist, be it destroyed or if the client does not have visibility."); } } //Not spawned. else { //Only look up asServer if not client, otherwise use client. bool asServer = !isClient; //Look up prefab. result = NetworkManager.GetPrefab(objectOrPrefabId, asServer); } #if DEVELOPMENT LastNetworkObject = result; #endif return result; } /// /// Reads a NetworkObjectId and nothing else. /// /// public int ReadNetworkObjectId() => (int)ReadSignedPackedWhole(); /// /// Reads the Id for a NetworkObject and outputs spawn settings. /// /// internal int ReadNetworkObjectForSpawn(out int initializeOrder, out ushort collectionid) { int objectId = ReadNetworkObjectId(); collectionid = ReadUInt16(); initializeOrder = ReadInt32(); return objectId; } /// /// Reads the Id for a NetworkObject and outputs despawn settings. /// /// internal int ReadNetworkObjectForDespawn(out DespawnType dt) { int objectId = ReadNetworkObjectId(); dt = (DespawnType)ReadUInt8Unpacked(); return objectId; } /// /// Reads a NetworkBehaviourId and ObjectId. /// /// internal byte ReadNetworkBehaviourId(out int objectId) { objectId = ReadNetworkObjectId(); if (objectId != NetworkObject.UNSET_OBJECTID_VALUE) return ReadUInt8Unpacked(); else return 0; } /// /// Reads a NetworkBehaviour. /// /// Objects which have been read to be spawned this tick, but may not have spawned yet. /// public NetworkBehaviour ReadNetworkBehaviour(out int objectId, out byte componentIndex, HashSet readSpawningObjects = null, bool logException = true) { NetworkObject nob = ReadNetworkObject(out objectId, readSpawningObjects, logException); componentIndex = ReadUInt8Unpacked(); NetworkBehaviour result; if (nob == null) { result = null; } else { if (componentIndex >= nob.NetworkBehaviours.Count) { NetworkManager.LogError($"ComponentIndex of {componentIndex} is out of bounds on {nob.gameObject.name} [id {nob.ObjectId}]. This may occur if you have modified your gameObject/prefab without saving it, or the scene."); result = null; } else { result = nob.NetworkBehaviours[componentIndex]; } } #if DEVELOPMENT LastNetworkBehaviour = result; #endif return result; } /// /// Reads a NetworkBehaviour. /// /// [DefaultReader] public NetworkBehaviour ReadNetworkBehaviour() { return ReadNetworkBehaviour(out _, out _); } public NetworkBehaviour ReadNetworkBehaviour(bool logException) { return ReadNetworkBehaviour(out _, out _, null, logException); } /// /// Reads a NetworkBehaviourId. /// public byte ReadNetworkBehaviourId() => ReadUInt8Unpacked(); /// /// Reads a DateTime. /// /// [DefaultReader] public DateTime ReadDateTime() { long value = (long)ReadSignedPackedWhole(); DateTime result = DateTime.FromBinary(value); return result; } /// /// Reads a transport channel. /// /// [DefaultReader] public Channel ReadChannel() { return (Channel)ReadUInt8Unpacked(); } /// /// Reads the Id for a NetworkConnection. /// /// public int ReadNetworkConnectionId() => (int)ReadSignedPackedWhole(); /// /// Reads a LayerMask. /// /// [DefaultReader] public LayerMask ReadLayerMask() { int layerValue = (int)ReadSignedPackedWhole(); return (LayerMask)layerValue; } /// /// Reads a NetworkConnection. /// /// [DefaultReader] public NetworkConnection ReadNetworkConnection() { int value = ReadNetworkConnectionId(); if (value == NetworkConnection.UNSET_CLIENTID_VALUE) { return FishNet.Managing.NetworkManager.EmptyConnection; } else { //Prefer server. if (NetworkManager.IsServerStarted) { NetworkConnection result; if (NetworkManager.ServerManager.Clients.TryGetValueIL2CPP(value, out result)) { return result; } //If also client then try client side data. else if (NetworkManager.IsClientStarted) { //If found in client collection then return. if (NetworkManager.ClientManager.Clients.TryGetValueIL2CPP(value, out result)) return result; /* Otherwise make a new instance. * We do not know if this is for the server or client so * initialize it either way. Connections rarely come through * without being in server/client side collection. */ else return new(NetworkManager, value, -1, true); } //Only server and not found. else { NetworkManager.LogWarning($"Unable to find connection for read Id {value}. An empty connection will be returned."); return FishNet.Managing.NetworkManager.EmptyConnection; } } //Try client side, will only be able to fetch against local connection. else { //If value is self then return self. if (value == NetworkManager.ClientManager.Connection.ClientId) return NetworkManager.ClientManager.Connection; //Try client side dictionary. else if (NetworkManager.ClientManager.Clients.TryGetValueIL2CPP(value, out NetworkConnection result)) return result; /* Otherwise make a new instance. * We do not know if this is for the server or client so * initialize it either way. Connections rarely come through * without being in server/client side collection. */ else return new(NetworkManager, value, -1, true); } } } /// /// Reads TransformProperties. /// [DefaultReader] public TransformProperties ReadTransformProperties() { Vector3 position = ReadVector3(); Quaternion rotation = ReadQuaternion32(); Vector3 scale = ReadVector3(); return new(position, rotation, scale); } /// /// Checks if the size could possibly be an allocation attack. /// /// private bool CheckAllocationAttack(int size) { /* Possible attacks. Impossible size, or size indicates * more elements in collection or more bytes needed * than what bytes are available. */ if (size != Writer.UNSET_COLLECTION_SIZE_VALUE && size < 0) { NetworkManager.LogError($"Size of {size} is invalid."); return false; } if (size > Remaining) { NetworkManager.LogError($"Read size of {size} is larger than remaining data of {Remaining}."); return false; } //Checks pass. return true; } /// /// Reads a state update packet. /// /// internal void ReadStateUpdatePacket(out uint clientTick) { clientTick = ReadTickUnpacked(); } #region Packed readers. /// /// ZigZag decode an integer. Move the sign bit back to the left. /// public ulong ZigZagDecode(ulong value) { ulong sign = value << 63; if (sign > 0) return ~(value >> 1) | sign; return value >> 1; } /// /// Reads a packed whole number and applies zigzag decoding. /// public long ReadSignedPackedWhole() => (long)ZigZagDecode(ReadUnsignedPackedWhole()); /// /// Reads a packed whole number. /// public ulong ReadUnsignedPackedWhole() { int shift = 0; ulong value = 0; /* Maximum number of bytes for ulong. * Prevents endless loop. Should not be neccessary but is a nice precaution. */ int maximumIterations = 10; int iterations = 0; int bufferLength = GetBuffer().Length; while (iterations < maximumIterations) { if (Position >= bufferLength) { NetworkManager.LogError($"Read position of {Position} is beyond reader's buffer length of {bufferLength}."); return 0; } byte currentByte = _buffer[Position++]; value |= (ulong)(currentByte & 0x7F) << shift; if ((currentByte & 0x80) == 0) break; shift += 7; iterations++; } return value; } #endregion #region Generators. /// /// Reads a reconcile. /// /// /// internal T ReadReconcile() => Read(); /// /// Reads a replicate along with it's past replicates into a collection. /// internal List> ReadReplicate(uint tick) where T : IReplicateData, new() { List> collection = CollectionCaches>.RetrieveList(); //Number of entries written. int count = (int)ReadUInt8Unpacked(); if (count <= 0) { NetworkManager.Log($"Replicate count cannot be 0 or less."); //Purge renaming and return default. Position += Remaining; return collection; } /* Subtract count total minus 1 * from starting tick. This sets the tick to what the first entry would be. * EG packet came in as tick 100, so that was passed as tick. * if there are 3 replicates then 2 would be subtracted (count - 1). * The new tick would be 98. * Ticks would be assigned to read values from oldest to * newest as 98, 99, 100. Which is the correct result. In order for this to * work properly past replicates cannot skip ticks. This will be ensured * in another part of the code. */ tick -= (uint)(count - 1); for (int i = 0; i < count; i++) { ReplicateDataContainer value = ReadReplicateData(tick + (uint)i); //Assign to collection. collection.Add(value); } return collection; } /// /// Reads a ReplicateData and applies tick and channel. /// private ReplicateDataContainer ReadReplicateData(uint tick) where T : IReplicateData, new() { T data = Read(); Channel c = ReadChannel(); return new(data, c, tick, isCreated: true); } /// /// Reads a collection using a collection from caches. /// public Dictionary ReadDictionary() { int count = (int)ReadSignedPackedWhole(); //Null collection. if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) return null; Dictionary collection = CollectionCaches.RetrieveDictionary(); ReadDictionary(count, collection); return collection; } /// /// Reads a collection. /// [Obsolete("Use ReadDictionary.")] public Dictionary ReadDictionaryAllocated() => ReadDictionary(); /// /// Reads into collection and returns item count read. /// /// /// True to allow the referenced collection to be nullified when receiving a null collection read. /// Number of values read into the collection. UNSET is returned if the collection were read as null. public int ReadDictionary(ref Dictionary collection, bool allowNullification = false) { int count = (int)ReadSignedPackedWhole(); //Null collection. if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) { if (allowNullification) collection = null; return count; } ReadDictionary(count, collection); return count; } /// /// Reads into a collection. /// private void ReadDictionary(int count, Dictionary collection) { if (count < 0) { NetworkManager.LogError($"Collection count cannot be less than 0."); //Purge renaming and return default. Position += Remaining; return; } if (collection == null) collection = new(count); else collection.Clear(); for (int i = 0; i < count; i++) { TKey key = Read(); TValue value = Read(); collection.Add(key, value); } } /// /// Reads a collection using a collection from caches. /// public List ReadList() { int count = (int)ReadSignedPackedWhole(); //Null collection. if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) return null; List result = CollectionCaches.RetrieveList(); ReadList(count, result); return result; } /// /// Reads a collection with allocations. /// [Obsolete("Use ReadList.")] public List ReadListAllocated() => ReadList(); /// /// Reads into collection and returns item count read. /// /// /// True to allow the referenced collection to be nullified when receiving a null collection read. /// Number of values read into the collection. UNSET is returned if the collection were read as null. public int ReadList(ref List collection, bool allowNullification = false) { int count = (int)ReadSignedPackedWhole(); //Null collection. if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) { if (allowNullification) collection = null; return count; } ReadList(count, collection); return count; } /// /// Reads into a collection. /// private void ReadList(int count, List collection) { if (count < 0) { NetworkManager.LogError($"List count cannot be less than 0."); //Purge renaming and return default. Position += Remaining; return; } if (collection == null) collection = new(count); else collection.Clear(); for (int i = 0; i < count; i++) collection.Add(Read()); } /// /// Reads a collection. /// public T[] ReadArrayAllocated() { T[] result = null; ReadArray(ref result); return result; } /// /// Reads into collection and returns amount read. /// /// /// /// public int ReadArray(ref T[] collection) { int count = (int)ReadSignedPackedWhole(); if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) return 0; if (count == 0) { if (collection == null) collection = new T[0]; return 0; } if (count < 0) { NetworkManager.Log($"Array count cannot be less than 0."); //Purge renaming and return default. Position += Remaining; return default; } //Initialize buffer if not already done. if (collection == null) collection = new T[count]; else if (collection.Length < count) Array.Resize(ref collection, count); for (int i = 0; i < count; i++) collection[i] = Read(); return count; } /// /// Reads any supported type as packed. /// public T Read() { Func del = GenericReader.Read; if (del == null) { NetworkManager.LogError($"Read method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer."); return default; } else { return del.Invoke(this); } } #endregion } }