using FishNet.CodeGenerating; using FishNet.Connection; using FishNet.Managing; using FishNet.Object; using FishNet.Object.Prediction; using FishNet.Serializing.Helping; using FishNet.Transporting; using FishNet.Utility; using GameKit.Dependencies.Utilities; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using GameKit.Dependencies.Utilities.Types; using UnityEngine; [assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] namespace FishNet.Serializing { /// /// Writes data to a buffer. /// public partial class Writer { #region Public. /// /// Capacity of the buffer. /// public int Capacity => _buffer.Length; /// /// Current write position. /// public int Position; /// /// Number of bytes writen to the buffer. /// public int Length; /// /// NetworkManager associated with this writer. May be null. /// public NetworkManager NetworkManager; #endregion #region Private. /// /// Buffer to prevent new allocations. This will grow as needed. /// private byte[] _buffer = new byte[64]; #endregion #region Const. /// /// Replicate data is default of T. /// internal const byte REPLICATE_DEFAULT_BYTE = 0; /// /// Replicate data is the same as the previous. /// internal const byte REPLICATE_DUPLICATE_BYTE = 1; /// /// Replicate data is different from the previous. /// internal const byte REPLICATE_UNIQUE_BYTE = 2; /// /// Replicate data is repeating for every entry. /// internal const byte REPLICATE_REPEATING_BYTE = 3; /// /// All datas in the replicate are default. /// internal const byte REPLICATE_ALL_DEFAULT_BYTE = 4; /// /// Value used when a collection is unset, as in null. /// public const int UNSET_COLLECTION_SIZE_VALUE = -1; #endregion /// /// Outputs writer to string. /// /// public override string ToString() => ToString(0, Length); /// /// Outputs writer 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)}."; } [Obsolete("Use Clear(NetworkManager) instead.")] public void Reset(NetworkManager newManager = null) => Clear(newManager); /// /// Resets written data. /// public void Clear() { Length = 0; Position = 0; } /// /// Resets written data and sets the NetworkManager. /// public void Clear(NetworkManager newManager) { Clear(); NetworkManager = newManager; } /// /// Ensures the buffer Capacity is of minimum count. /// /// public void EnsureBufferCapacity(int count) { if (Capacity < count) Array.Resize(ref _buffer, count); } /// /// Ensure a number of bytes to be available in the buffer from current position. /// /// public void EnsureBufferLength(int count) { if (Position + count > _buffer.Length) { int nextSize = (_buffer.Length * 2) + count; Array.Resize(ref _buffer, nextSize); } } /// /// Returns the buffer. The returned value will be the full buffer, even if not all of it is used. /// /// public byte[] GetBuffer() { return _buffer; } /// /// Returns the used portion of the buffer as an ArraySegment. /// /// public ArraySegment GetArraySegment() { return new(_buffer, 0, Length); } /// /// Reserves a number of bytes from current position. /// /// [Obsolete("Use Skip.")] public void Reserve(int count) => Skip(count); /// /// Skips a number of bytes from current position. /// /// public void Skip(int count) { EnsureBufferLength(count); Position += count; Length = Math.Max(Length, Position); } /// /// Sets size variables back an amount. /// /// internal void Remove(int count) { Position -= count; Length -= count; } /// /// Sends a packetId. /// /// internal void WritePacketIdUnpacked(PacketId pid) { WriteUInt16Unpacked((ushort)pid); } /// /// Inserts a packetId. /// internal void InsertPacketIdUnpacked(PacketId packetId, int index) { ushort pId = (ushort)packetId; InsertUInt16Unpacked(pId, index); } /// /// Inserts value at index within the buffer. /// This method does not perform error checks nor increases Length, Position. /// [Obsolete("Use InsertUInt8Unpacked.")] public void FastInsertUInt8Unpacked(byte value, int index) => InsertUInt8Unpacked(value, index); /// /// Inserts value at index within the buffer. /// This method does not perform error checks nor increases Length, Position. /// public void InsertUInt8Unpacked(byte value, int index) { _buffer[index] = value; } /// /// Inserts value at index within the buffer. /// This method does not perform error checks nor increases Length, Position. /// public void InsertUInt16Unpacked(ushort value, int index) { _buffer[index++] = (byte)value; _buffer[index] = (byte)(value >> 8); } /// /// Inserts value at index within the buffer. /// This method does not perform error checks nor increases Length, Position. /// public void InsertInt32Unpacked(int value, int index) => InsertUInt32Unpacked((uint)value, index); /// /// Inserts value at index within the buffer. /// This method does not perform error checks nor increases Length, Position. /// public void InsertUInt32Unpacked(uint value, int index) { _buffer[index++] = (byte)value; _buffer[index++] = (byte)(value >> 8); _buffer[index++] = (byte)(value >> 16); _buffer[index] = (byte)(value >> 24); } [Obsolete("Use WriteUInt8Unpacked.")] public void WriteByte(byte value) => WriteUInt8Unpacked(value); /// /// Writes a byte. /// /// [DefaultWriter] public void WriteUInt8Unpacked(byte value) { EnsureBufferLength(1); _buffer[Position++] = value; Length = Math.Max(Length, Position); } [Obsolete("Use WriteUInt8Array.")] public void WriteBytes(byte[] value, int offset, int count) => WriteUInt8Array(value, offset, count); /// /// Writes bytes. /// /// /// /// public void WriteUInt8Array(byte[] value, int offset, int count) { EnsureBufferLength(count); Buffer.BlockCopy(value, offset, _buffer, Position, count); Position += count; Length = Math.Max(Length, Position); } [Obsolete("Use WriteUInt8ArrayAndSize.")] public void WriteBytesAndSize(byte[] value, int offset, int count) => WriteUInt8ArrayAndSize(value, offset, count); /// /// Writes bytes and length of bytes. /// /// /// /// public void WriteUInt8ArrayAndSize(byte[] value, int offset, int count) { if (value == null) { WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); } else { WriteInt32(count); WriteUInt8Array(value, offset, count); } } [Obsolete("Use WriteUInt8ArrayAndSize.")] public void WriteBytesAndSize(byte[] value) => WriteUInt8ArrayAndSize(value); /// /// Writes all bytes in value and length of bytes. /// /// public void WriteUInt8ArrayAndSize(byte[] value) { int size = (value == null) ? 0 : value.Length; // buffer might be null, so we can't use .Length in that case WriteUInt8ArrayAndSize(value, 0, size); } [Obsolete("Use WriteInt8Unpacked.")] public void WriteSByte(sbyte value) => WriteInt8Unpacked(value); /// /// Writes a sbyte. /// [DefaultWriter] public void WriteInt8Unpacked(sbyte value) => WriteUInt8Unpacked((byte)value); /// /// Writes a char. /// /// [DefaultWriter] public void WriteChar(char value) { EnsureBufferLength(2); _buffer[Position++] = (byte)value; _buffer[Position++] = (byte)(value >> 8); Length = Math.Max(Length, Position); } /// /// Writes a boolean. /// /// [DefaultWriter] public void WriteBoolean(bool value) { EnsureBufferLength(1); _buffer[Position++] = (value) ? (byte)1 : (byte)0; Length = Math.Max(Length, Position); } /// /// Writes a uint16 unpacked. /// /// public void WriteUInt16Unpacked(ushort value) { EnsureBufferLength(2); _buffer[Position++] = (byte)value; _buffer[Position++] = (byte)(value >> 8); Length = Math.Max(Length, Position); } /// /// Writes a uint16. /// /// //todo: should be using WritePackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. [DefaultWriter] public void WriteUInt16(ushort value) => WriteUInt16Unpacked(value); /// /// Writes a int16 unpacked. /// /// //todo: should be WritePackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. public void WriteInt16Unpacked(short value) => WriteUInt16Unpacked((ushort)value); /// /// Writes a int16. /// /// //todo: should be WritePackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. [DefaultWriter] public void WriteInt16(short value) => WriteUInt16Unpacked((ushort)value); /// /// Writes a int32. /// /// public void WriteInt32Unpacked(int value) => WriteUInt32Unpacked((uint)value); /// /// Writes an int32. /// /// [DefaultWriter] public void WriteInt32(int value) => WriteSignedPackedWhole(value); /// /// Writes value to dst without error checking. /// internal static void WriteUInt32Unpacked(byte[] dst, uint value, ref int position) { dst[position++] = (byte)value; dst[position++] = (byte)(value >> 8); dst[position++] = (byte)(value >> 16); dst[position++] = (byte)(value >> 24); } /// /// Writes a uint32. /// /// public void WriteUInt32Unpacked(uint value) { EnsureBufferLength(4); WriteUInt32Unpacked(_buffer, value, ref Position); Length = Math.Max(Length, Position); } /// /// Writes a uint32. /// /// [DefaultWriter] public void WriteUInt32(uint value) => WriteUnsignedPackedWhole(value); /// /// Writes a uint64. /// /// public void WriteUInt64Unpacked(ulong value) { EnsureBufferLength(8); _buffer[Position++] = (byte)value; _buffer[Position++] = (byte)(value >> 8); _buffer[Position++] = (byte)(value >> 16); _buffer[Position++] = (byte)(value >> 24); _buffer[Position++] = (byte)(value >> 32); _buffer[Position++] = (byte)(value >> 40); _buffer[Position++] = (byte)(value >> 48); _buffer[Position++] = (byte)(value >> 56); Length = Math.Max(Position, Length); } /// /// Writes a uint64. /// /// [DefaultWriter] public void WriteUInt64(ulong value) => WriteUnsignedPackedWhole(value); /// /// Writes a int64. /// /// public void WriteInt64Unpacked(long value) => WriteUInt64((ulong)value); /// /// Writes an int64. /// /// [DefaultWriter] public void WriteInt64(long value) => WriteSignedPackedWhole(value); /// /// Writes a single (float). /// /// public void WriteSingleUnpacked(float value) { EnsureBufferLength(4); UIntFloat converter = new() { FloatValue = value }; WriteUInt32Unpacked(converter.UIntValue); } /// /// Writes a single (float). /// /// [DefaultWriter] public void WriteSingle(float value) => WriteSingleUnpacked(value); /// /// Writes a double. /// /// public void WriteDoubleUnpacked(double value) { UIntDouble converter = new() { DoubleValue = value }; WriteUInt64Unpacked(converter.LongValue); } /// /// Writes a double. /// /// [DefaultWriter] public void WriteDouble(double value) => WriteDoubleUnpacked(value); /// /// Writes a decimal. /// /// public void WriteDecimalUnpacked(decimal value) { UIntDecimal converter = new() { DecimalValue = value }; WriteUInt64Unpacked(converter.LongValue1); WriteUInt64Unpacked(converter.LongValue2); } /// /// Writes a decimal. /// /// [DefaultWriter] public void WriteDecimal(decimal value) => WriteDecimalUnpacked(value); /// /// Writes a string. /// /// [DefaultWriter] public void WriteString(string value) { if (value == null) { WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); return; } byte[] buffer = Strings.Buffer; int length = value.ToBytes(ref buffer); WriteInt32(length); //Nothing to write. if (length == 0) return; WriteUInt8Array(buffer, 0, length); } /// /// Writes a byte ArraySegment and it's size. /// /// [DefaultWriter] public void WriteArraySegmentAndSize(ArraySegment value) => WriteUInt8ArrayAndSize(value.Array, value.Offset, value.Count); /// /// Writes an ArraySegment without size. /// /// public void WriteArraySegment(ArraySegment value) => WriteUInt8Array(value.Array, value.Offset, value.Count); /// /// Writes a Vector2. /// /// public void WriteVector2Unpacked(Vector2 value) { WriteSingleUnpacked(value.x); WriteSingleUnpacked(value.y); } /// /// Writes a Vector2. /// /// [DefaultWriter] public void WriteVector2(Vector2 value) => WriteVector2Unpacked(value); /// /// Writes a Vector3 /// /// public void WriteVector3Unpacked(Vector3 value) { WriteSingleUnpacked(value.x); WriteSingleUnpacked(value.y); WriteSingleUnpacked(value.z); } /// /// Writes a Vector3 /// /// [DefaultWriter] public void WriteVector3(Vector3 value) => WriteVector3Unpacked(value); /// /// Writes a Vector4. /// /// public void WriteVector4Unpacked(Vector4 value) { WriteSingleUnpacked(value.x); WriteSingleUnpacked(value.y); WriteSingleUnpacked(value.z); WriteSingleUnpacked(value.w); } /// /// Writes a Vector4. /// /// [DefaultWriter] public void WriteVector4(Vector4 value) => WriteVector4Unpacked(value); /// /// Writes a Vector2Int. /// /// public void WriteVector2IntUnpacked(Vector2Int value) { WriteInt32Unpacked(value.x); WriteInt32Unpacked(value.y); } /// /// Writes a Vector2Int. /// /// [DefaultWriter] public void WriteVector2Int(Vector2Int value) { WriteSignedPackedWhole(value.x); WriteSignedPackedWhole(value.y); } /// /// Writes a Vector3Int. /// /// public void WriteVector3IntUnpacked(Vector3Int value) { WriteInt32Unpacked(value.x); WriteInt32Unpacked(value.y); WriteInt32Unpacked(value.z); } /// /// Writes a Vector3Int. /// /// [DefaultWriter] public void WriteVector3Int(Vector3Int value) { WriteSignedPackedWhole(value.x); WriteSignedPackedWhole(value.y); WriteSignedPackedWhole(value.z); } /// /// Writes a Color. /// /// public void WriteColorUnpacked(Color value) { WriteSingleUnpacked(value.r); WriteSingleUnpacked(value.g); WriteSingleUnpacked(value.b); WriteSingleUnpacked(value.a); } /// /// Writes a Color. /// /// [DefaultWriter] public void WriteColor(Color value) { EnsureBufferLength(4); _buffer[Position++] = (byte)(value.r * 100f); _buffer[Position++] = (byte)(value.g * 100f); _buffer[Position++] = (byte)(value.b * 100f); _buffer[Position++] = (byte)(value.a * 100f); Length = Math.Max(Length, Position); } /// /// Writes a Color32. /// /// [DefaultWriter] public void WriteColor32(Color32 value) { EnsureBufferLength(4); _buffer[Position++] = value.r; _buffer[Position++] = value.g; _buffer[Position++] = value.b; _buffer[Position++] = value.a; Length = Math.Max(Length, Position); } /// /// Writes a Quaternion. /// /// public void WriteQuaternionUnpacked(Quaternion value) { WriteSingleUnpacked(value.x); WriteSingleUnpacked(value.y); WriteSingleUnpacked(value.z); WriteSingleUnpacked(value.w); } /// /// Writes a Quaternion. /// /// public void WriteQuaternion64(Quaternion value) { ulong result = Quaternion64Compression.Compress(value); WriteUInt64Unpacked(result); } /// /// Writes a Quaternion. /// /// [DefaultWriter] public void WriteQuaternion32(Quaternion value) { Quaternion32Compression.Compress(this, value); } /// /// Reads a Quaternion. /// /// internal void WriteQuaternion(Quaternion value, AutoPackType autoPackType) { switch (autoPackType) { case AutoPackType.Packed: WriteQuaternion32(value); ; break; case AutoPackType.PackedLess: WriteQuaternion64(value); break; default: WriteQuaternionUnpacked(value); break; } } /// /// Writes a rect. /// /// public void WriteRectUnpacked(Rect value) { WriteSingleUnpacked(value.xMin); WriteSingleUnpacked(value.yMin); WriteSingleUnpacked(value.width); WriteSingleUnpacked(value.height); } /// /// Writes a rect. /// /// [DefaultWriter] public void WriteRect(Rect value) => WriteRectUnpacked(value); /// /// Writes a plane. /// /// public void WritePlaneUnpacked(Plane value) { WriteVector3Unpacked(value.normal); WriteSingleUnpacked(value.distance); } /// /// Writes a plane. /// /// [DefaultWriter] public void WritePlane(Plane value) => WritePlaneUnpacked(value); /// /// Writes a Ray. /// /// public void WriteRayUnpacked(Ray value) { WriteVector3Unpacked(value.origin); WriteVector3Unpacked(value.direction); } /// /// Writes a Ray. /// /// [DefaultWriter] public void WriteRay(Ray value) => WriteRayUnpacked(value); /// /// Writes a Ray2D. /// /// public void WriteRay2DUnpacked(Ray2D value) { WriteVector2Unpacked(value.origin); WriteVector2Unpacked(value.direction); } /// /// Writes a Ray2D. /// /// [DefaultWriter] public void WriteRay2D(Ray2D value) => WriteRay2DUnpacked(value); /// /// Writes a Matrix4x4. /// /// public void WriteMatrix4x4Unpacked(Matrix4x4 value) { WriteSingleUnpacked(value.m00); WriteSingleUnpacked(value.m01); WriteSingleUnpacked(value.m02); WriteSingleUnpacked(value.m03); WriteSingleUnpacked(value.m10); WriteSingleUnpacked(value.m11); WriteSingleUnpacked(value.m12); WriteSingleUnpacked(value.m13); WriteSingleUnpacked(value.m20); WriteSingleUnpacked(value.m21); WriteSingleUnpacked(value.m22); WriteSingleUnpacked(value.m23); WriteSingleUnpacked(value.m30); WriteSingleUnpacked(value.m31); WriteSingleUnpacked(value.m32); WriteSingleUnpacked(value.m33); } /// /// Writes a Matrix4x4. /// /// [DefaultWriter] public void WriteMatrix4x4(Matrix4x4 value) => WriteMatrix4x4Unpacked(value); /// /// Writes a Guid. /// /// [DefaultWriter] public void WriteGuidAllocated(System.Guid value) { byte[] data = value.ToByteArray(); WriteUInt8Array(data, 0, data.Length); } /// /// Writes a tick without packing. /// /// public void WriteTickUnpacked(uint value) => WriteUInt32Unpacked(value); /// /// Writes a GameObject. GameObject must be spawned over the network already or be a prefab with a NetworkObject attached. /// /// [DefaultWriter] public void WriteGameObject(GameObject go) { //There needs to be a header to indicate if null, nob, or nb. if (go == null) { WriteUInt8Unpacked(0); } else { //Try to write the NetworkObject first. if (go.TryGetComponent(out NetworkObject nob)) { WriteUInt8Unpacked(1); WriteNetworkObject(nob); } //If there was no nob try to write a NetworkBehaviour. else if (go.TryGetComponent(out NetworkBehaviour nb)) { WriteUInt8Unpacked(2); WriteNetworkBehaviour(nb); } //Object cannot be serialized so write null. else { WriteUInt8Unpacked(0); NetworkManager.LogError($"GameObject {go.name} cannot be serialized because it does not have a NetworkObject nor NetworkBehaviour."); } } } /// /// Writes a Transform. Transform must be spawned over the network already or be a prefab with a NetworkObject attached. /// /// [DefaultWriter] public void WriteTransform(Transform t) { if (t == null) { WriteNetworkObject(null); } else { NetworkObject nob = t.GetComponent(); WriteNetworkObject(nob); } } /// /// Writes a NetworkObject.ObjectId. /// /// public void WriteNetworkObjectId(NetworkObject nob) { int id = (nob == null) ? NetworkObject.UNSET_OBJECTID_VALUE : nob.ObjectId; WriteNetworkObjectId(id); } /// /// Writes a NetworkObject while optionally including the initialization order. /// [DefaultWriter] public void WriteNetworkObject(NetworkObject nob) { if (nob == null) { WriteNetworkObjectId(NetworkObject.UNSET_OBJECTID_VALUE); } else { bool spawned = nob.IsSpawned; if (spawned) WriteNetworkObjectId(nob.ObjectId); else WriteNetworkObjectId(nob.PrefabId); /* Spawned is written after because it's only needed if nob * is not null. If it were written before it would also have * to be written when nob == null.*/ WriteBoolean(spawned); } } /// /// Writes a spawned networkObject. /// internal void WriteSpawnedNetworkObject(NetworkObject nob) { WriteNetworkObjectId(nob.ObjectId); WriteUInt16(nob.SpawnableCollectionId); WriteInt32(nob.GetInitializeOrder()); } /// /// Writes a NetworkObject for a despawn message. /// /// /// internal void WriteNetworkObjectForDespawn(NetworkObject nob, DespawnType dt) { WriteNetworkObjectId(nob.ObjectId); WriteUInt8Unpacked((byte)dt); } /// /// Writes an objectId. /// public void WriteNetworkObjectId(int objectId) => WriteSignedPackedWhole(objectId); /// /// Writes a NetworkBehaviour. /// /// [DefaultWriter] public void WriteNetworkBehaviour(NetworkBehaviour nb) { if (nb == null) { WriteNetworkObject(null); WriteUInt8Unpacked(0); } else { WriteNetworkObject(nb.NetworkObject); WriteUInt8Unpacked(nb.ComponentIndex); } } /// /// Writes a NetworkBehaviourId. /// public void WriteNetworkBehaviourId(NetworkBehaviour nb) { if (nb == null) WriteUInt8Unpacked(NetworkBehaviour.UNSET_NETWORKBEHAVIOUR_ID); else WriteUInt8Unpacked(nb.ComponentIndex); } /// /// Writes a DateTime. /// [DefaultWriter] public void WriteDateTime(DateTime dt) => WriteSignedPackedWhole(dt.ToBinary()); /// /// Writes a transport channel. /// /// [DefaultWriter] public void WriteChannel(Channel channel) => WriteUInt8Unpacked((byte)channel); /// /// Writers a LayerMask. /// /// [DefaultWriter] public void WriteLayerMask(LayerMask value) => WriteSignedPackedWhole(value.value); /// /// Writes a NetworkConnection. /// /// [DefaultWriter] public void WriteNetworkConnection(NetworkConnection connection) { int value = (connection == null) ? NetworkConnection.UNSET_CLIENTID_VALUE : connection.ClientId; WriteNetworkConnectionId(value); } /// /// Writes TransformProperties. /// [DefaultWriter] public void WriteTransformProperties(TransformProperties value) { WriteVector3(value.Position); WriteQuaternion32(value.Rotation); WriteVector3(value.Scale); } /// /// Writes a short for a connectionId. /// /// public void WriteNetworkConnectionId(int id) => WriteSignedPackedWhole(id); /// /// Writes a dictionary. /// public void WriteDictionary(Dictionary dict) { if (dict == null) { WriteSignedPackedWhole(Writer.UNSET_COLLECTION_SIZE_VALUE); return; } else { WriteSignedPackedWhole(dict.Count); } foreach (KeyValuePair item in dict) { Write(item.Key); Write(item.Value); } } /// /// Writes a list. /// /// Collection to write. public void WriteList(List value) { int count = (value == null) ? 0 : value.Count; WriteList(value, 0, count); } /// /// Writes a state update packet. /// /// internal void WriteStateUpdatePacket(uint lastPacketTick) => WriteTickUnpacked(lastPacketTick); #region Packed writers. /// /// ZigZag encode an integer. Move the sign bit to the right. /// public ulong ZigZagEncode(ulong value) { if (value >> 63 > 0) return ~(value << 1) | 1; return value << 1; } /// /// Writes a packed whole number. /// /// public void WriteSignedPackedWhole(long value) => WriteUnsignedPackedWhole(ZigZagEncode((ulong)value)); /// /// Writes a packed whole number. /// /// /// /// Writes a packed whole number. /// /// public void WriteUnsignedPackedWhole(ulong value) { EnsureBufferLength(9); while (value > 127) { _buffer[Position++] = (byte)((value & 0x7F) | 0x80); value >>= 7; } _buffer[Position++] = (byte)(value & 0x7F); Length = Math.Max(Length, Position); } #endregion #region Generators. /// /// Writes a list. /// /// Collection to write. /// Offset to begin at. /// Entries to write. public void WriteList(List value, int offset, int count) { if (value == null) { WriteSignedPackedWhole(Writer.UNSET_COLLECTION_SIZE_VALUE); } else { //Make sure values cannot cause out of bounds. if ((offset + count > value.Count)) count = 0; WriteSignedPackedWhole(count); for (int i = 0; i < count; i++) Write(value[i + offset]); } } /// /// Writes a list. /// /// Collection to write. /// Offset to begin at. public void WriteList(List value, int offset) { int count = (value == null) ? 0 : value.Count; WriteList(value, offset, count - offset); } /// /// Writes an array. /// /// Collection to write. public void WriteArray(T[] value) { int count = (value == null) ? 0 : value.Length; WriteArray(value, 0, count); } /// /// Writes an array. /// /// Collection to write. /// Offset to begin at. public void WriteArray(T[] value, int offset) { int count = (value == null) ? 0 : value.Length; WriteArray(value, offset, count - offset); } /// /// Writes an array. /// /// Collection to write. /// Offset to begin at. /// Entries to write. public void WriteArray(T[] value, int offset, int count) { if (value == null) { WriteSignedPackedWhole(Writer.UNSET_COLLECTION_SIZE_VALUE); } else { //If theres no values, or offset exceeds count then write 0 for count. if (value.Length == 0 || (offset >= count)) { WriteSignedPackedWhole(0); } else { WriteSignedPackedWhole(count); for (int i = offset; i < count; i++) Write(value[i]); } } } /// /// Writes a reconcile. /// internal void WriteReconcile(T data) { Write(data); } /// /// Writes a replication to the server. /// internal void WriteReplicate(RingBuffer> values, int offset) where T : IReplicateData, new() { /* COUNT * * Each Entry: * 0 if the same as previous. * 1 if default. */ int collectionCount = values.Count; //Replicate list will never be null, no need to write null check. //Number of entries being written. byte count = (byte)(collectionCount - offset); WriteUInt8Unpacked(count); for (int i = offset; i < collectionCount; i++) WriteReplicateDataContainer(values[i]); } internal void WriteReplicate(BasicQueue> values, int redundancyCount) where T : IReplicateData, new() { /* COUNT * * Each Entry: * 0 if the same as previous. * 1 if default. */ int collectionCount = values.Count; //Replicate list will never be null, no need to write null check. //Number of entries being written. byte count = (byte)redundancyCount; WriteUInt8Unpacked(count); for (int i = (collectionCount - redundancyCount); i < collectionCount; i++) WriteReplicateDataContainer(values[i]); } /// /// Reads a ReplicateData and applies tick and channel. /// private void WriteReplicateDataContainer(ReplicateDataContainer value) where T : IReplicateData , new() { Write(value.Data); WriteChannel(value.Channel); } /// /// Writes any supported type using packing. /// public void Write(T value) { Action del = GenericWriter.Write; if (del == null) NetworkManager.LogError($"Write method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer."); else del.Invoke(this, value); } #endregion } }