using FishNet.Connection; using FishNet.Documenting; using FishNet.Managing; using FishNet.Object; using FishNet.Serializing.Helping; using FishNet.Transporting; using FishNet.Utility.Constant; using FishNet.Utility.Performance; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; [assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] namespace FishNet.Serializing { /// /// Used for write references to generic types. /// /// [APIExclude] public static class GenericWriter { public static Action Write { get; set; } public static Action WriteAutoPack { get; set; } } /// /// Writes data to a buffer. /// public 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 /// /// Resets the writer as though it was unused. Does not reset buffers. /// public void Reset(NetworkManager manager = null) { Length = 0; Position = 0; NetworkManager = manager; } /// /// Writes a dictionary. /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteDictionary(Dictionary dict) { if (dict == null) { WriteBoolean(true); return; } else { WriteBoolean(false); } WriteInt32(dict.Count); foreach (KeyValuePair item in dict) { Write(item.Key); Write(item.Value); } } /// /// 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. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] 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 ArraySegment(_buffer, 0, Length); } /// /// Reserves a number of bytes from current position. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reserve(int count) { EnsureBufferLength(count); Position += count; Length = Math.Max(Length, Position); } /// /// Writes length. This method is used to make debugging easier. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteLength(int length) { WriteInt32(length); } /// /// Sends a packetId. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WritePacketId(PacketId pid) { WriteUInt16((ushort)pid); } /// /// Inserts value at index within the buffer. /// This method does not perform error checks. /// /// /// [CodegenExclude] public void FastInsertByte(byte value, int index) { _buffer[index] = value; } /// /// Writes a byte. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteByte(byte value) { EnsureBufferLength(1); _buffer[Position++] = value; Length = Math.Max(Length, Position); } /// /// Writes bytes. /// /// /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteBytes(byte[] value, int offset, int count) { EnsureBufferLength(count); Buffer.BlockCopy(value, offset, _buffer, Position, count); Position += count; Length = Math.Max(Length, Position); } /// /// Writes bytes and length of bytes. /// /// /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteBytesAndSize(byte[] value, int offset, int count) { if (value == null) { WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); } else { WriteInt32(count); WriteBytes(value, offset, count); } } /// /// Writes all bytes in value and length of bytes. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteBytesAndSize(byte[] value) { int size = (value == null) ? 0 : value.Length; // buffer might be null, so we can't use .Length in that case WriteBytesAndSize(value, 0, size); } /// /// Writes a sbyte. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteSByte(sbyte value) { EnsureBufferLength(1); _buffer[Position++] = (byte)value; Length = Math.Max(Length, Position); } /// /// Writes a char. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteChar(char value) { EnsureBufferLength(2); _buffer[Position++] = (byte)value; _buffer[Position++] = (byte)(value >> 8); Length = Math.Max(Length, Position); } /// /// Writes a boolean. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteBoolean(bool value) { EnsureBufferLength(1); _buffer[Position++] = (value) ? (byte)1 : (byte)0; Length = Math.Max(Length, Position); } /// /// Writes a uint16. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt16(ushort value) { EnsureBufferLength(2); _buffer[Position++] = (byte)value; _buffer[Position++] = (byte)(value >> 8); Length = Math.Max(Length, Position); } /// /// Writes a int16. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteInt16(short value) { EnsureBufferLength(2); _buffer[Position++] = (byte)value; _buffer[Position++] = (byte)(value >> 8); Length = Math.Max(Length, Position); } /// /// Writes a int32. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteInt32(int value, AutoPackType packType = AutoPackType.Packed) { if (packType == AutoPackType.Packed) WritePackedWhole(ZigZagEncode((ulong)value)); else WriteUInt32((uint)value, packType); } /// /// Writes a uint32. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt32(uint value, AutoPackType packType = AutoPackType.Packed) { if (packType == AutoPackType.Unpacked) { EnsureBufferLength(4); WriterExtensions.WriteUInt32(_buffer, value, ref Position); Length = Math.Max(Length, Position); } else { WritePackedWhole(value); } } /// /// Writes an int64. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteInt64(long value, AutoPackType packType = AutoPackType.Packed) { if (packType == AutoPackType.Packed) WritePackedWhole(ZigZagEncode((ulong)value)); else WriteUInt64((ulong)value, packType); } /// /// Writes a uint64. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt64(ulong value, AutoPackType packType = AutoPackType.Packed) { if (packType == AutoPackType.Unpacked) { 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); } else { WritePackedWhole(value); } } /// /// Writes a single (float). /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteSingle(float value, AutoPackType packType = AutoPackType.Unpacked) { if (packType == AutoPackType.Unpacked) { UIntFloat converter = new UIntFloat { FloatValue = value }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); } else { long converter = (long)(value * 100f); WritePackedWhole((ulong)converter); } } /// /// Writes a double. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteDouble(double value) { UIntDouble converter = new UIntDouble { DoubleValue = value }; WriteUInt64(converter.LongValue, AutoPackType.Unpacked); } /// /// Writes a decimal. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteDecimal(decimal value) { UIntDecimal converter = new UIntDecimal { DecimalValue = value }; WriteUInt64(converter.LongValue1, AutoPackType.Unpacked); WriteUInt64(converter.LongValue2, AutoPackType.Unpacked); } /// /// Writes a string. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteString(string value) { if (value == null) { WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); return; } else if (value.Length == 0) { WriteInt32(0); return; } /* Resize string buffer as needed. There's no harm in * increasing buffer on writer side because sender will * never intentionally inflict allocations on itself. * Reader ensures string count cannot exceed received * packet size. */ int size; byte[] stringBuffer = WriterStatics.GetStringBuffer(value, out size); WriteInt32(size); WriteBytes(stringBuffer, 0, size); } /// /// Writes a byte ArraySegment and it's size. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteArraySegmentAndSize(ArraySegment value) { WriteBytesAndSize(value.Array, value.Offset, value.Count); } /// /// Writes an ArraySegment without size. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteArraySegment(ArraySegment value) { WriteBytes(value.Array, value.Offset, value.Count); } /// /// Writes a Vector2. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector2(Vector2 value) { UIntFloat converter; converter = new UIntFloat { FloatValue = value.x }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); converter = new UIntFloat { FloatValue = value.y }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); } /// /// Writes a Vector3 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector3(Vector3 value) { UIntFloat converter; converter = new UIntFloat { FloatValue = value.x }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); converter = new UIntFloat { FloatValue = value.y }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); converter = new UIntFloat { FloatValue = value.z }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); } /// /// Writes a Vector4. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector4(Vector4 value) { UIntFloat converter; converter = new UIntFloat { FloatValue = value.x }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); converter = new UIntFloat { FloatValue = value.y }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); converter = new UIntFloat { FloatValue = value.z }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); converter = new UIntFloat { FloatValue = value.w }; WriteUInt32(converter.UIntValue, AutoPackType.Unpacked); } /// /// Writes a Vector2Int. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector2Int(Vector2Int value, AutoPackType packType = AutoPackType.Packed) { WriteInt32(value.x, packType); WriteInt32(value.y, packType); } /// /// Writes a Vector3Int. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector3Int(Vector3Int value, AutoPackType packType = AutoPackType.Packed) { WriteInt32(value.x, packType); WriteInt32(value.y, packType); WriteInt32(value.z, packType); } /// /// Writes a Color. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteColor(Color value, AutoPackType packType = AutoPackType.Packed) { if (packType == AutoPackType.Unpacked) { WriteSingle(value.r); WriteSingle(value.g); WriteSingle(value.b); WriteSingle(value.a); } else { 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. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] 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. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteQuaternion(Quaternion value, AutoPackType packType = AutoPackType.Packed) { if (packType == AutoPackType.Packed) { EnsureBufferLength(4); uint result = Quaternion32Compression.Compress(value); WriterExtensions.WriteUInt32(_buffer, result, ref Position); Length = Math.Max(Length, Position); } else if (packType == AutoPackType.PackedLess) { EnsureBufferLength(8); ulong result = Quaternion64Compression.Compress(value); WriterExtensions.WriteUInt64(_buffer, result, ref Position); Length = Math.Max(Length, Position); } else { EnsureBufferLength(16); WriteSingle(value.x); WriteSingle(value.y); WriteSingle(value.z); WriteSingle(value.w); } } /// /// Writes a rect. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteRect(Rect value) { WriteSingle(value.xMin); WriteSingle(value.yMin); WriteSingle(value.width); WriteSingle(value.height); } /// /// Writes a plane. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WritePlane(Plane value) { WriteVector3(value.normal); WriteSingle(value.distance); } /// /// Writes a Ray. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteRay(Ray value) { WriteVector3(value.origin); WriteVector3(value.direction); } /// /// Writes a Ray2D. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteRay2D(Ray2D value) { WriteVector2(value.origin); WriteVector2(value.direction); } /// /// Writes a Matrix4x4. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteMatrix4x4(Matrix4x4 value) { WriteSingle(value.m00); WriteSingle(value.m01); WriteSingle(value.m02); WriteSingle(value.m03); WriteSingle(value.m10); WriteSingle(value.m11); WriteSingle(value.m12); WriteSingle(value.m13); WriteSingle(value.m20); WriteSingle(value.m21); WriteSingle(value.m22); WriteSingle(value.m23); WriteSingle(value.m30); WriteSingle(value.m31); WriteSingle(value.m32); WriteSingle(value.m33); } /// /// Writes a Guid. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteGuidAllocated(System.Guid value) { byte[] data = value.ToByteArray(); WriteBytes(data, 0, data.Length); } /// /// Writes a GameObject. GameObject must be spawned over the network already or be a prefab with a NetworkObject attached. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteGameObject(GameObject go) { if (go == null) { WriteNetworkObject(null); } else { NetworkObject nob = go.GetComponent(); WriteNetworkObject(nob); } } /// /// Writes a Transform. Transform must be spawned over the network already or be a prefab with a NetworkObject attached. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteTransform(Transform t) { if (t == null) { WriteNetworkObject(null); } else { NetworkObject nob = t.GetComponent(); WriteNetworkObject(nob); } } /// /// Writes a NetworkObject. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteNetworkObject(NetworkObject nob) { WriteNetworkObject(nob, false); } /// /// Writes a NetworkObject.ObjectId. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteNetworkObjectId(NetworkObject nob) { if (nob == null) WriteUInt16(NetworkObject.UNSET_OBJECTID_VALUE); else WriteNetworkObjectId(nob.ObjectId); } /// /// Writes a NetworkObject while optionally including the initialization order. /// /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteNetworkObject(NetworkObject nob, bool forSpawn) { if (nob == null) { WriteUInt16(NetworkObject.UNSET_OBJECTID_VALUE); } else { bool spawned = nob.IsSpawned; if (spawned) WriteNetworkObjectId(nob.ObjectId); else WriteNetworkObjectId(nob.PrefabId); //Has to be written after objectId since that's expected first in reader. if (forSpawn) { WriteUInt16(nob.SpawnableCollectionId); WriteSByte(nob.GetInitializeOrder()); } WriteBoolean(spawned); } } /// /// Writes a NetworkObject for a despawn message. /// /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteNetworkObjectForDespawn(NetworkObject nob, DespawnType dt) { WriteNetworkObjectId(nob.ObjectId); WriteByte((byte)dt); } /// /// Writes an objectId. /// [CodegenExclude] public void WriteNetworkObjectId(int objectId) { WriteUInt16((ushort)objectId); } /// /// Writes a NetworkObject for a spawn packet. /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteNetworkObjectForSpawn(NetworkObject nob) { WriteNetworkObject(nob, true); } /// /// Writes a NetworkBehaviour. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteNetworkBehaviour(NetworkBehaviour nb) { if (nb == null) { WriteNetworkObject(null); WriteByte(0); } else { WriteNetworkObject(nb.NetworkObject); WriteByte(nb.ComponentIndex); } } /// /// Writes a NetworkBehaviourId. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteNetworkBehaviourId(NetworkBehaviour nb) { if (nb == null) { WriteNetworkObjectId(null); } else { WriteNetworkObjectId(nb.NetworkObject); WriteByte(nb.ComponentIndex); } } /// /// Writes a DateTime. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteDateTime(DateTime dt) { WriteInt64(dt.ToBinary()); } /// /// Writes a transport channel. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteChannel(Channel channel) { WriteByte((byte)channel); } /// /// Writes a NetworkConnection. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteNetworkConnection(NetworkConnection connection) { int value = (connection == null) ? NetworkConnection.UNSET_CLIENTID_VALUE : connection.ClientId; WriteInt16((short)value); } /// /// Writes a short for a connectionId. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteNetworkConnectionId(short id) { WriteInt16(id); } /// /// Writes a ListCache. /// /// ListCache to write. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteListCache(ListCache lc) { WriteList(lc.Collection); } /// /// Writes a list. /// /// Collection to write. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteList(List value) { if (value == null) WriteList(null, 0, 0); else WriteList(value, 0, value.Count); } #region Packed writers. /// /// ZigZag encode an integer. Move the sign bit to the right. /// [CodegenExclude] public ulong ZigZagEncode(ulong value) { if (value >> 63 > 0) return ~(value << 1) | 1; return value << 1; } /// /// Writes a packed whole number. /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WritePackedWhole(ulong value) { if (value < 0x80UL) { EnsureBufferLength(1); _buffer[Position++] = (byte)(value & 0x7F); } else if (value < 0x4000UL) { EnsureBufferLength(2); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)((value >> 7) & 0x7F); } else if (value < 0x200000UL) { EnsureBufferLength(3); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)((value >> 14) & 0x7F); } else if (value < 0x10000000UL) { EnsureBufferLength(4); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); _buffer[Position++] = (byte)((value >> 21) & 0x7F); } else if (value < 0x100000000UL) { EnsureBufferLength(5); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); _buffer[Position++] = (byte)((value >> 28) & 0x0F); } else if (value < 0x10000000000UL) { EnsureBufferLength(6); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); _buffer[Position++] = (byte)(0x10 | ((value >> 28) & 0x0F)); _buffer[Position++] = (byte)((value >> 32) & 0xFF); } else if (value < 0x1000000000000UL) { EnsureBufferLength(7); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); _buffer[Position++] = (byte)(0x20 | ((value >> 28) & 0x0F)); _buffer[Position++] = (byte)((value >> 32) & 0xFF); _buffer[Position++] = (byte)((value >> 40) & 0xFF); } else if (value < 0x100000000000000UL) { EnsureBufferLength(8); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); _buffer[Position++] = (byte)(0x30 | ((value >> 28) & 0x0F)); _buffer[Position++] = (byte)((value >> 32) & 0xFF); _buffer[Position++] = (byte)((value >> 40) & 0xFF); _buffer[Position++] = (byte)((value >> 48) & 0xFF); } else { EnsureBufferLength(9); _buffer[Position++] = (byte)(0x80 | (value & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 7) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 14) & 0x7F)); _buffer[Position++] = (byte)(0x80 | ((value >> 21) & 0x7F)); _buffer[Position++] = (byte)(0x40 | ((value >> 28) & 0x0F)); _buffer[Position++] = (byte)((value >> 32) & 0xFF); _buffer[Position++] = (byte)((value >> 40) & 0xFF); _buffer[Position++] = (byte)((value >> 48) & 0xFF); _buffer[Position++] = (byte)((value >> 56) & 0xFF); } Length = Math.Max(Length, Position); } #endregion #region Generators. /// /// Writes a list. /// /// Collection to write. /// Offset to begin at. /// Entries to write. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteList(List value, int offset, int count) { if (value == null) { WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); } else { //Make sure values cannot cause out of bounds. if ((offset + count > value.Count)) count = 0; WriteInt32(count); for (int i = 0; i < count; i++) Write(value[i + offset]); } } /// /// Writes a list. /// /// Collection to write. /// Offset to begin at. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteList(List value, int offset) { if (value == null) WriteList(null, 0, 0); else WriteList(value, offset, value.Count - offset); } /// /// Writes a replication to the server. /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteReplicate(List values, int offset) { /* 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); WriteByte(count); //Get comparer. Func compareDel = GeneratedComparer.Compare; Func isDefaultDel = GeneratedComparer.IsDefault; if (compareDel == null || isDefaultDel == null) { LogError($"ReplicateComparers not found for type {typeof(T).FullName}"); return; } T lastData = default; /* It's possible to save more bytes by writing that they are all the same. * Run a check, and if they are all the same then only write * the first data with the same indicator code. */ byte fullPackType = 0; bool repeating = true; bool allDefault = true; for (int i = offset; i < collectionCount; i++) { T v = values[i]; if (!isDefaultDel(v)) allDefault = false; //Only check if i is larger than offset, giving us something in the past to check. if (i > offset) { //Not repeating. bool match = compareDel.Invoke(v, values[i - 1]); if (!match) { repeating = false; break; } } } if (allDefault) fullPackType = REPLICATE_ALL_DEFAULT_BYTE; else if (repeating) fullPackType = REPLICATE_REPEATING_BYTE; WriteByte(fullPackType); //If repeating only write the first entry. if (repeating) { //Only write if not default. if (!allDefault) Write(values[offset]); } //Otherwise check each entry for differences. else { for (int i = offset; i < collectionCount; i++) { T v = values[i]; bool isDefault = isDefaultDel.Invoke(v); //Default data, easy exit on writes. if (isDefault) { WriteByte(REPLICATE_DEFAULT_BYTE); } //Data is not default. else { //Same as last data. bool match = compareDel.Invoke(v, lastData); if (match) { WriteByte(REPLICATE_DUPLICATE_BYTE); } else { WriteByte(REPLICATE_UNIQUE_BYTE); Write(v); lastData = v; } } } } } /// /// Writes an array. /// /// Collection to write. /// Offset to begin at. /// Entries to write. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteArray(T[] value, int offset, int count) { if (value == null) { WriteInt32(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)) { WriteInt32(0); } else { WriteInt32(count); for (int i = offset; i < count; i++) Write(value[i]); } } } /// /// Writes an array. /// /// Collection to write. /// Offset to begin at. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteArray(T[] value, int offset) { if (value == null) WriteArray(null, 0, 0); else WriteArray(value, offset, value.Length - offset); } /// /// Writes an array. /// /// Collection to write. [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteArray(T[] value) { if (value == null) WriteArray(null, 0, 0); else WriteArray(value, 0, value.Length); } /// /// Writers any supported type. /// /// /// [CodegenExclude] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(T value) { System.Type type = typeof(T); if (IsAutoPackType(type, out AutoPackType packType)) { Action del = GenericWriter.WriteAutoPack; if (del == null) LogError(GetLogMessage()); else del.Invoke(this, value, packType); } else { Action del = GenericWriter.Write; if (del == null) LogError(GetLogMessage()); else del.Invoke(this, value); } string GetLogMessage() => $"Write method not found for {type.FullName}. Use a supported type or create a custom serializer."; } /// /// Logs an error. /// /// private void LogError(string msg) { if (NetworkManager == null) NetworkManager.StaticLogError(msg); else NetworkManager.LogError(msg); } /// /// Returns if T takes AutoPackType argument. /// /// Outputs the default pack type for T. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsAutoPackType(out AutoPackType packType) { System.Type type = typeof(T); return IsAutoPackType(type, out packType); } internal static bool IsAutoPackType(Type type, out AutoPackType packType) { if (WriterExtensions.DefaultPackedTypes.Contains(type)) { packType = AutoPackType.Packed; return true; } else if (type == typeof(float)) { packType = AutoPackType.Unpacked; return true; } else { packType = AutoPackType.Unpacked; return false; } } #endregion } }