From 2fa0f6d3f34b13cef84997204a4a26ff960dd816 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 16 Aug 2021 19:03:59 -0500 Subject: [PATCH 01/58] FastBufferWriter implemented and tested (still need to add and update some xmldoc comments though) --- .../Runtime/Serialization.meta | 8 + .../Runtime/Serialization/FastBufferWriter.cs | 254 ++ .../Serialization/FastBufferWriter.cs.meta | 11 + .../Runtime/Serialization/BitCounter.cs | 106 + .../Runtime/Serialization/BitCounter.cs.meta | 11 + .../Runtime/Serialization/BitWriter.cs | 190 ++ .../Runtime/Serialization/BitWriter.cs.meta | 11 + .../Runtime/Serialization/BytePacker.cs | 565 +++++ .../Runtime/Serialization/BytePacker.cs.meta | 11 + .../Runtime/Serialization/FastBufferWriter.cs | 835 +++++++ .../Serialization/FastBufferWriter.cs.meta | 11 + .../Serialization/SerializationTypeTable.cs | 100 + .../SerializationTypeTable.cs.meta | 11 + .../Tests/Editor/Serialization.meta | 3 + .../Editor/Serialization/BitCounterTests.cs | 63 + .../Serialization/BitCounterTests.cs.meta | 11 + .../Editor/Serialization/BitWriterTests.cs | 178 ++ .../Serialization/BitWriterTests.cs.meta | 11 + .../Editor/Serialization/BytePackerTests.cs | 788 ++++++ .../Serialization/BytePackerTests.cs.meta | 11 + .../Serialization/FastBufferWriterTests.cs | 2123 +++++++++++++++++ .../FastBufferWriterTests.cs.meta | 11 + .../com.unity.netcode.editortests.asmdef | 21 +- .../Assets/StreamingAssets/BuildInfo.json | 1 + .../StreamingAssets/BuildInfo.json.meta | 7 + 25 files changed, 5347 insertions(+), 5 deletions(-) create mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization.meta create mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs create mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta create mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json create mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization.meta new file mode 100644 index 0000000000..7ec99bd274 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fd73dafe8658fb4cb3c87f77e783073 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs new file mode 100644 index 0000000000..ddf56fcd51 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs @@ -0,0 +1,254 @@ +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; + +namespace Unity.Multiplayer.Netcode +{ + public struct FastBufferWriter + { + private NativeArray m_buffer; + private readonly unsafe byte* m_bufferPointer; + private int m_position; + + public unsafe FastBufferWriter(NativeArray buffer, int position = 0) + { + m_buffer = buffer; + m_bufferPointer = (byte*) m_buffer.GetUnsafePtr(); + m_position = position; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Seek(int where) + { + m_position = where; + } + + public int Position => m_position; + + public NativeArray GetNativeArray() + { + return m_buffer; + } + + public byte[] ToArray() + { + return m_buffer.ToArray(); + } + + public unsafe byte* GetUnsafePtr() + { + return m_bufferPointer; + } + + public unsafe byte* GetUnsafePtrAtCurrentPosition() + { + return m_bufferPointer + m_position; + } + + /// + /// Write single-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteSinglePacked(float value) + { + WriteUInt32Packed(ToUint(value)); + } + + /// + /// Write double-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteDoublePacked(double value) + { + WriteUInt64Packed(ToUlong(value)); + } + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt16Packed(short value) => WriteInt64Packed(value); + + /// + /// Write an unsigned short (UInt16) as a varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt16Packed(ushort value) => WriteUInt64Packed(value); + + /// + /// Write a two-byte character as a varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteCharPacked(char c) => WriteUInt16Packed(c); + + /// + /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt32Packed(int value) => WriteInt64Packed(value); + + /// + /// Write an unsigned int (UInt32) as a varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt32Packed(uint value) => WriteUInt64Packed(value); + + /// + /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt64Packed(long value) => WriteUInt64Packed(Arithmetic.ZigZagEncode(value)); + + /// + /// Write an unsigned long (UInt64) as a varint to the stream. + /// + /// Value to write + public void WriteUInt64Packed(ulong value) + { + if (value <= 240) + { + WriteULongByte(value); + } + else if (value <= 2287) + { + WriteULongByte(((value - 240) >> 8) + 241); + WriteULongByte(value - 240); + } + else if (value <= 67823) + { + WriteByte(249); + WriteULongByte((value - 2288) >> 8); + WriteULongByte(value - 2288); + } + else + { + ulong header = 255; + ulong match = 0x00FF_FFFF_FFFF_FFFFUL; + while (value <= match) + { + --header; + match >>= 8; + } + + WriteULongByte(header); + int max = (int)(header - 247); + for (int i = 0; i < max; ++i) + { + WriteULongByte(value >> (i << 3)); + } + } + } + + /// + /// Write a byte (in an int format) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteIntByte(int value) => WriteByte((byte)value); + + /// + /// Write a byte (in a ulong format) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteULongByte(ulong byteValue) => WriteByte((byte)byteValue); + + /// + /// Write a byte to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteByte(byte value) + { + m_bufferPointer[m_position++] = value; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytes(byte* value, int size) + { + UnsafeUtility.MemCpy((m_bufferPointer + m_position), value, size); + m_position += size; + } + + /// + /// Copy the contents of this writer into another writer. + /// The contents will be copied from the beginning of this writer to its current position. + /// They will be copied to the other writer starting at the other writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyTo(FastBufferWriter other) + { + other.WriteBytes(m_bufferPointer, m_position); + } + + /// + /// Copy the contents of another writer into this writer. + /// The contents will be copied from the beginning of the other writer to its current position. + /// They will be copied to this writer starting at this writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyFrom(FastBufferWriter other) + { + WriteBytes(other.m_bufferPointer, other.m_position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe uint ToUint(T value) where T : unmanaged + { + uint* asUint = (uint*) &value; + return *asUint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe ulong ToUlong(T value) where T : unmanaged + { + ulong* asUlong = (ulong*) &value; + return *asUlong; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe float ToSingle(T value) where T : unmanaged + { + float* asFloat = (float*) &value; + return *asFloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe double ToDouble(T value) where T : unmanaged + { + double* asDouble = (double*) &value; + return *asDouble; + } + + /// + /// Write a value of any unmanaged type to the buffer. + /// It will be copied into the buffer exactly as it exists in memory. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(in T value) where T : unmanaged + { + int len = sizeof(T); + T* pointer = (T*)(m_bufferPointer+m_position); + *pointer = value; + m_position += len; + } + } +} \ No newline at end of file diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta new file mode 100644 index 0000000000..6a6eb1001b --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6cac99a38cd00a41a020d7f46dfb0f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs new file mode 100644 index 0000000000..a357077bb5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -0,0 +1,106 @@ +using System.Runtime.CompilerServices; + +namespace Unity.Multiplayer.Netcode +{ + public static class BitCounter + { + // Since we don't have access to BitOperations.LeadingZeroCount() (which would have been the fastest) + // we use the De Bruijn sequence to do this calculation + // See https://en.wikipedia.org/wiki/De_Bruijn_sequence and https://www.chessprogramming.org/De_Bruijn_Sequence + private const ulong k_DeBruijnMagic64 = 0x37E84A99DAE458F; + private const uint k_DeBruijnMagic32 = 0x06EB14F9; + + // We're counting bytes, not bits, so these have all had the operation x/8 + 1 applied + private static readonly int[] k_deBruijnTableBytes64 = + { + 0/8+1, 1/8+1, 17/8+1, 2/8+1, 18/8+1, 50/8+1, 3/8+1, 57/8+1, + 47/8+1, 19/8+1, 22/8+1, 51/8+1, 29/8+1, 4/8+1, 33/8+1, 58/8+1, + 15/8+1, 48/8+1, 20/8+1, 27/8+1, 25/8+1, 23/8+1, 52/8+1, 41/8+1, + 54/8+1, 30/8+1, 38/8+1, 5/8+1, 43/8+1, 34/8+1, 59/8+1, 8/8+1, + 63/8+1, 16/8+1, 49/8+1, 56/8+1, 46/8+1, 21/8+1, 28/8+1, 32/8+1, + 14/8+1, 26/8+1, 24/8+1, 40/8+1, 53/8+1, 37/8+1, 42/8+1, 7/8+1, + 62/8+1, 55/8+1, 45/8+1, 31/8+1, 13/8+1, 39/8+1, 36/8+1, 6/8+1, + 61/8+1, 44/8+1, 12/8+1, 35/8+1, 60/8+1, 11/8+1, 10/8+1, 9/8+1, + }; + + private static readonly int[] k_deBruijnTableBytes32 = + { + 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, + 30/8+1, 20/8+1, 18/8+1, 11/8+1, 13/8+1, 4/8+1, 7/8+1, 23/8+1, + 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, + 14/8+1, 27/8+1, 9/8+1, 5/8+1, 26/8+1, 8/8+1, 25/8+1, 24/8+1, + }; + + // And here we're counting the number of set bits, not the position of the highest set, + // so these still have +1 applied - unfortunately 0 and 1 both return the same value. + private static readonly int[] k_deBruijnTableBits64 = + { + 0+1, 1+1, 17+1, 2+1, 18+1, 50+1, 3+1, 57+1, + 47+1, 19+1, 22+1, 51+1, 29+1, 4+1, 33+1, 58+1, + 15+1, 48+1, 20+1, 27+1, 25+1, 23+1, 52+1, 41+1, + 54+1, 30+1, 38+1, 5+1, 43+1, 34+1, 59+1, 8+1, + 63+1, 16+1, 49+1, 56+1, 46+1, 21+1, 28+1, 32+1, + 14+1, 26+1, 24+1, 40+1, 53+1, 37+1, 42+1, 7+1, + 62+1, 55+1, 45+1, 31+1, 13+1, 39+1, 36+1, 6+1, + 61+1, 44+1, 12+1, 35+1, 60+1, 11+1, 10+1, 9+1, + }; + + private static readonly int[] k_deBruijnTableBits32 = + { + 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, + 30+1, 20+1, 18+1, 11+1, 13+1, 4+1, 7+1, 23+1, + 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, + 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedByteCount(uint b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b = b & ~(b >> 1); + return k_deBruijnTableBytes32[b*k_DeBruijnMagic32 >> 27]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedByteCount(ulong b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b |= b >> 32; + b = b & ~(b >> 1); + return k_deBruijnTableBytes64[b*k_DeBruijnMagic64 >> 58]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedBitCount(uint b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b = b & ~(b >> 1); + return k_deBruijnTableBits32[b*k_DeBruijnMagic32 >> 27]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedBitCount(ulong b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b |= b >> 32; + b = b & ~(b >> 1); + return k_deBruijnTableBits64[b*k_DeBruijnMagic64 >> 58]; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta new file mode 100644 index 0000000000..f9d96d15b6 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6983de23935090341bf45d5564401b9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs new file mode 100644 index 0000000000..8b28d3d80d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -0,0 +1,190 @@ +using System; +using System.Runtime.CompilerServices; +using Mono.Cecil; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct BitWriter + { + private unsafe FastBufferWriter.InternalData* m_InternalData; + private unsafe byte* m_BufferPointer; + private int m_Position; + private int m_BitPosition; + private const int BITS_PER_BYTE = 8; + + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + public bool BitAligned { get => (m_BitPosition & 7) == 0; } + + internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) + { + fixed (FastBufferWriter.InternalData* internalDataPtr = &internalData) + { + m_InternalData = internalDataPtr; + } + + m_BufferPointer = internalData.BufferPointer + internalData.Position; + m_Position = internalData.Position; + m_BitPosition = 0; + } + + public unsafe void Dispose() + { + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned) + { + // Accounting for the partial write + ++bytesWritten; + } + + m_InternalData->CommitBitwiseWrites(bytesWritten); + } + + /// + /// Write s certain amount of bits to the stream. + /// + /// Value to get bits from. + /// Amount of bits to write + public unsafe void WriteBits(ulong value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (bitCount > 64) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write more than 64 bits from a 64-bit value!"); + } + + if (bitCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); + } + + int checkPos = (m_BitPosition + bitCount) >> 3; + if (checkPos > m_InternalData->AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + int wholeBytes = bitCount / BITS_PER_BYTE; + byte* asBytes = (byte*) &value; + if (BitAligned) + { + WritePartialValue(value, wholeBytes); + } + else + { + for (var i = 0; i < wholeBytes; ++i) + { + WriteMisaligned(asBytes[i]); + } + } + + for (var count = wholeBytes * BITS_PER_BYTE; count < bitCount; ++count) + { + WriteBit((value & (1UL << count)) != 0); + } + } + + /// + /// Write bits to stream. + /// + /// Value to get bits from. + /// Amount of bits to write. + public unsafe void WriteBits(byte value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_BitPosition + bitCount) >> 3; + if (checkPos >= m_InternalData->AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + for (int i = 0; i < bitCount; ++i) + { + WriteBit(((value >> i) & 1) != 0); + } + } + + /// + /// Write a single bit to the buffer + /// + /// Value of the bit. True represents 1, False represents 0 + public unsafe void WriteBit(bool bit) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_BitPosition + 1) >> 3; + if (checkPos >= m_InternalData->AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + int offset = m_BitPosition & 7; + int pos = m_BitPosition >> 3; + ++m_BitPosition; + m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + + byte* ptr = ((byte*) &value) + offsetBytes; + byte* bufferPointer = m_BufferPointer + m_Position; + switch (bytesToWrite) + { + case 1: + bufferPointer[0] = *ptr; + break; + case 2: + *(ushort*) bufferPointer = *(ushort*)ptr; + break; + case 3: + *(ushort*) bufferPointer = *(ushort*)ptr; + *(bufferPointer+2) = *(ptr+2); + break; + case 4: + *(uint*) bufferPointer = *(uint*)ptr; + break; + case 5: + *(uint*) bufferPointer = *(uint*)ptr; + *(bufferPointer+4) = *(ptr+4); + break; + case 6: + *(uint*) bufferPointer = *(uint*) &ptr; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + break; + case 7: + *(uint*) bufferPointer = *(uint*) &value; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + *(bufferPointer+6) = *(ptr+6); + break; + case 8: + *(ulong*) bufferPointer = *(ulong*)ptr; + break; + default: + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); + break; + } + + m_BitPosition += bytesToWrite * BITS_PER_BYTE; + } + + private unsafe void WriteMisaligned(byte value) + { + int off = (int)(m_BitPosition & 7); + int pos = m_BitPosition >> 3; + int shift1 = 8 - off; + m_BufferPointer[pos + 1] = (byte)((m_BufferPointer[pos + 1] & (0xFF << off)) | (value >> shift1)); + m_BufferPointer[pos] = (byte)((m_BufferPointer[pos] & (0xFF >> shift1)) | (value << off)); + + m_BitPosition += 8; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta new file mode 100644 index 0000000000..604982f3c0 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d6360e096142c149a11a2e86560c350 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs new file mode 100644 index 0000000000..bf458eee20 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -0,0 +1,565 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class BytePacker + { + #region Managed TypePacking + /// + /// Writes a boxed object in a packed format + /// Named differently from other WriteValuePacked methods to avoid accidental boxing + /// + /// The object to write + public static void WriteObjectPacked(ref FastBufferWriter writer, object value, bool isNullable = false) + { +#if UNITY_NETCODE_DEBUG_NO_PACKING + writer.WriteObject(value, isNullable); + return; +#endif + if (isNullable || value.GetType().IsNullable()) + { + bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + + WriteValuePacked(ref writer, isNull); + + if (isNull) + { + return; + } + } + + var type = value.GetType(); + var hasSerializer = SerializationTypeTable.SerializersPacked.TryGetValue(type, out var serializer); + if (hasSerializer) + { + serializer(ref writer, value); + return; + } + + if (value is Array array) + { + WriteValuePacked(ref writer, array.Length); + + for (int i = 0; i < array.Length; i++) + { + WriteObjectPacked(ref writer, array.GetValue(i)); + } + } + + if (value.GetType().IsEnum) + { + switch (Convert.GetTypeCode(value)) + { + case TypeCode.Boolean: + WriteValuePacked(ref writer, (byte)value); + break; + case TypeCode.Char: + WriteValuePacked(ref writer, (char)value); + break; + case TypeCode.SByte: + WriteValuePacked(ref writer, (sbyte)value); + break; + case TypeCode.Byte: + WriteValuePacked(ref writer, (byte)value); + break; + case TypeCode.Int16: + WriteValuePacked(ref writer, (short)value); + break; + case TypeCode.UInt16: + WriteValuePacked(ref writer, (ushort)value); + break; + case TypeCode.Int32: + WriteValuePacked(ref writer, (int)value); + break; + case TypeCode.UInt32: + WriteValuePacked(ref writer, (uint)value); + break; + case TypeCode.Int64: + WriteValuePacked(ref writer, (long)value); + break; + case TypeCode.UInt64: + WriteValuePacked(ref writer, (ulong)value); + break; + } + return; + } + if (value is GameObject) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValuePacked(ref writer, networkObject.NetworkObjectId); + return; + } + if (value is NetworkObject) + { + if (!((NetworkObject)value).IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); + } + + WriteValuePacked(ref writer, ((NetworkObject)value).NetworkObjectId); + return; + } + if (value is NetworkBehaviour) + { + if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkObjectId); + WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkBehaviourId); + return; + } + if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + } + + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + } + #endregion + + #region Unmanaged Type Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteValuePacked(T value) where T: unmanaged => writer.WriteValue(value); +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteValuePacked(ref FastBufferWriter writer, ref TEnum value) where TEnum : unmanaged, Enum + { + TEnum enumValue = value; + switch (sizeof(TEnum)) + { + case sizeof(int): + WriteValuePacked(ref writer, *(int*)&enumValue); + break; + case sizeof(byte): + WriteValuePacked(ref writer, *(byte*)&enumValue); + break; + case sizeof(short): + WriteValuePacked(ref writer, *(short*)&enumValue); + break; + case sizeof(long): + WriteValuePacked(ref writer, *(long*)&enumValue); + break; + } + } + + /// + /// Write single-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, float value) + { + WriteUInt32Packed(ref writer, ToUint(value)); + } + + /// + /// Write double-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, double value) + { + WriteUInt64Packed(ref writer, ToUlong(value)); + } + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByte(value); + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValue(value); + + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, short value) => WriteUInt32Packed(ref writer, (ushort)Arithmetic.ZigZagEncode(value)); + + /// + /// Write an unsigned short (UInt16) as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, ushort value) => WriteUInt32Packed(ref writer, value); + + /// + /// Write a two-byte character as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, char c) => WriteUInt32Packed(ref writer, c); + + /// + /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, int value) => WriteUInt32Packed(ref writer, (uint)Arithmetic.ZigZagEncode(value)); + + /// + /// Write an unsigned int (UInt32) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, uint value) => WriteUInt32Packed(ref writer, value); + + /// + /// Write an unsigned long (UInt64) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, ulong value) => WriteUInt64Packed(ref writer, value); + + /// + /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, long value) => WriteUInt64Packed(ref writer, (ulong)Arithmetic.ZigZagEncode(value)); + + /// + /// Convenience method that writes two packed Vector3 from the ray to the stream + /// + /// Ray to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Ray ray) + { + WriteValuePacked(ref writer, ray.origin); + WriteValuePacked(ref writer, ray.direction); + } + + /// + /// Convenience method that writes two packed Vector2 from the ray to the stream + /// + /// Ray2D to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Ray2D ray2d) + { + WriteValuePacked(ref writer, ray2d.origin); + WriteValuePacked(ref writer, ray2d.direction); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Color color) + { + WriteValuePacked(ref writer, color.r); + WriteValuePacked(ref writer, color.g); + WriteValuePacked(ref writer, color.b); + WriteValuePacked(ref writer, color.a); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Color32 color) + { + WriteValuePacked(ref writer, color.r); + WriteValuePacked(ref writer, color.g); + WriteValuePacked(ref writer, color.b); + WriteValuePacked(ref writer, color.a); + } + + /// + /// Convenience method that writes two varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Vector2 vector2) + { + WriteValuePacked(ref writer, vector2.x); + WriteValuePacked(ref writer, vector2.y); + } + + /// + /// Convenience method that writes three varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Vector3 vector3) + { + WriteValuePacked(ref writer, vector3.x); + WriteValuePacked(ref writer, vector3.y); + WriteValuePacked(ref writer, vector3.z); + } + + /// + /// Convenience method that writes four varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4) + { + WriteValuePacked(ref writer, vector4.x); + WriteValuePacked(ref writer, vector4.y); + WriteValuePacked(ref writer, vector4.z); + WriteValuePacked(ref writer, vector4.w); + } + + /// + /// Writes the rotation to the stream. + /// + /// Rotation to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rotation) + { + if (Mathf.Sign(rotation.w) < 0) + { + WriteValuePacked(ref writer, -rotation.x); + WriteValuePacked(ref writer, -rotation.y); + WriteValuePacked(ref writer, -rotation.z); + } + else + { + WriteValuePacked(ref writer, rotation.x); + WriteValuePacked(ref writer, rotation.y); + WriteValuePacked(ref writer, rotation.z); + } + } + + /// + /// Writes a string in a packed format + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, string s) + { + WriteValuePacked(ref writer, (uint)s.Length); + int target = s.Length; + for (int i = 0; i < target; ++i) + { + WriteValuePacked(ref writer, s[i]); + } + } +#endif + #endregion + + #region Bit Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteValueBitPacked(T value) where T: unmanaged => writer.WriteValue(value); +#else + public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); + + public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value) + { + if (value >= 0b1000_0000_0000_0000) + { + throw new ArgumentException("BitPacked ushorts must be <= 15 bits"); + } + + if (value <= 0b0111_1111) + { + writer.WriteByte((byte)(value << 1)); + return; + } + + writer.WriteValue((ushort)((value << 1) | 0b1)); + } + + public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint) Arithmetic.ZigZagEncode(value)); + + public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) + { + if (value >= 0b0100_0000_0000_0000_0000_0000_0000_0000) + { + throw new ArgumentException("BitPacked uints must be <= 30 bits"); + } + + if (value <= 0b0011_1111) + { + writer.WriteByte((byte)(value << 2)); + return; + } + + if (value <= 0b0011_1111_1111_1111) + { + writer.WriteValue((ushort)((value << 2) | 0b01)); + return; + } + + if (value <= 0b0011_1111_1111_1111_1111_1111) + { + writer.WritePartialValue(((value << 2) | 0b10), 3); + return; + } + + writer.WriteValue(((value << 2) | 0b11)); + } + + public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, (ulong) Arithmetic.ZigZagEncode(value)); + + public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) + { + if (value >= 0b0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000) + { + throw new ArgumentException("BitPacked ulongs must be <= 61 bits"); + } + + if (value <= 0b0001_1111) + { + writer.WriteByte((byte)(value << 3)); + return; + } + + if (value <= 0b0001_1111_1111_1111) + { + writer.WriteValue((ushort)((value << 3) | 0b001)); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b010, 3); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) + { + writer.WriteValue((uint)((value << 3) | 0b011)); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b100, 5); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b101, 6); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b110, 7); + return; + } + + writer.WriteValue((value << 3) | 0b111); + } +#endif + #endregion + + #region Private Methods + private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) + { + if (value <= 240) + { + writer.WriteByte((byte)value); + return; + } + if (value <= 2287) + { + writer.WriteByte((byte)(((value - 240) >> 8) + 241)); + writer.WriteByte((byte)(value - 240)); + return; + } + var writeBytes = BitCounter.GetUsedByteCount(value); + writer.WriteByte((byte)(247 + writeBytes)); + writer.WritePartialValue(value, writeBytes); + } + + // Looks like the same code as WriteUInt64Packed? + // It's actually different because it will call the more efficient 32-bit version + // of BytewiseUtility.GetUsedByteCount(). + private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) + { + if (value <= 240) + { + writer.WriteByte((byte)value); + return; + } + if (value <= 2287) + { + writer.WriteByte((byte)(((value - 240) >> 8) + 241)); + writer.WriteByte((byte)(value - 240)); + return; + } + var writeBytes = BitCounter.GetUsedByteCount(value); + writer.WriteByte((byte)(247 + writeBytes)); + writer.WritePartialValue(value, writeBytes); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint ToUint(T value) where T : unmanaged + { + uint* asUint = (uint*) &value; + return *asUint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong ToUlong(T value) where T : unmanaged + { + ulong* asUlong = (ulong*) &value; + return *asUlong; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float ToSingle(T value) where T : unmanaged + { + float* asFloat = (float*) &value; + return *asFloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe double ToDouble(T value) where T : unmanaged + { + double* asDouble = (double*) &value; + return *asDouble; + } + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta new file mode 100644 index 0000000000..dfb5e75613 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a3ec13587ae68cb49b82af8612d47698 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs new file mode 100644 index 0000000000..c6b88f3b60 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -0,0 +1,835 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public struct FastBufferWriter : IDisposable + { + private NativeArray m_buffer; + + internal struct InternalData + { + public unsafe byte* BufferPointer; + public int Position; + public int Length; + public int Capacity; + public int MaxCapacity; + public Allocator Allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + public int AllowedWriteMark; + public bool InBitwiseContext; +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseWrites(int amount) + { + Position += amount; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + InBitwiseContext = false; +#endif + } + } + + private InternalData m_InternalData; + + public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + var buffer = new NativeArray(size, allocator, NativeArrayOptions.ClearMemory); +#else + var buffer = new NativeArray(size, allocator, NativeArrayOptions.UninitializedMemory); +#endif + m_buffer = buffer; + m_InternalData = new InternalData + { + BufferPointer = (byte*) m_buffer.GetUnsafePtr(), + Position = 0, + Length = 0, + Capacity = buffer.Length, + Allocator = allocator, + MaxCapacity = maxSize == -1 ? size : maxSize, +#if DEVELOPMENT_BUILD || UNITY_EDITOR + AllowedWriteMark = 0, + InBitwiseContext = false, +#endif + }; + } + + public void Dispose() + { + m_buffer.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Seek(int where) + { + // This avoids us having to synchronize length all the time. + // Writing things is a much more common operation than seeking + // or querying length. The length here is a high watermark of + // what's been written. So before we seek, if the current position + // is greater than the length, we update that watermark. + // When querying length later, we'll return whichever of the two + // values is greater, thus if we write past length, length increases + // because position increases, and if we seek backward, length remembers + // the position it was in. + // Seeking forward will not update the length. + if (m_InternalData.Position > m_InternalData.Length && where < m_InternalData.Position) + { + m_InternalData.Length = m_InternalData.Position; + } + m_InternalData.Position = where; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Truncate(int where = -1) + { + if (where == -1) + { + where = Position; + } + + if (m_InternalData.Position > where) + { + m_InternalData.Position = where; + } + if(m_InternalData.Length > where) + { + m_InternalData.Length = where; + } + } + + public unsafe BitWriter EnterBitwiseContext() + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.InBitwiseContext = true; +#endif + return new BitWriter(ref m_InternalData); + } + + public int Position => m_InternalData.Position; + public int Capacity => m_InternalData.Capacity; + public int Length => m_InternalData.Position > m_InternalData.Length ? m_InternalData.Position : m_InternalData.Length; + + private unsafe void Grow() + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + var buffer = new NativeArray(Math.Min(m_InternalData.Capacity * 2, m_InternalData.MaxCapacity), m_InternalData.Allocator, NativeArrayOptions.ClearMemory); +#else + var buffer = new NativeArray(m_InternalData.Capacity * 2, m_InternalData.Allocator, NativeArrayOptions.UninitializedMemory); +#endif + UnsafeUtility.MemCpy(buffer.GetUnsafePtr(), m_InternalData.BufferPointer, Length); + m_buffer.Dispose(); + m_buffer = buffer; + m_InternalData.BufferPointer = (byte*) m_buffer.GetUnsafePtr(); + m_InternalData.Capacity = buffer.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool VerifyCanWrite(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + int len = sizeof(T); + if (m_InternalData.Position + len > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedWriteMark = m_InternalData.Position + len; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NativeArray GetNativeArray() + { + return m_buffer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] ToArray() + { + return m_buffer.ToArray(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtr() + { + return m_InternalData.BufferPointer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtrAtCurrentPosition() + { + return m_InternalData.BufferPointer + m_InternalData.Position; + } + + /// + /// Writes a boxed object in a standard format + /// Named differently from other WriteValue methods to avoid accidental boxing + /// + /// The object to write + public void WriteObject(object value, bool isNullable = false) + { + if (isNullable || value.GetType().IsNullable()) + { + bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + + WriteValueSafe(isNull); + + if (isNull) + { + return; + } + } + + var type = value.GetType(); + var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); + if (hasSerializer) + { + serializer(ref this, value); + return; + } + + if (value is Array array) + { + WriteValueSafe(array.Length); + + for (int i = 0; i < array.Length; i++) + { + WriteObject(array.GetValue(i)); + } + + return; + } + + if (value.GetType().IsEnum) + { + switch (Convert.GetTypeCode(value)) + { + case TypeCode.Boolean: + WriteValueSafe((byte)value); + break; + case TypeCode.Char: + WriteValueSafe((char)value); + break; + case TypeCode.SByte: + WriteValueSafe((sbyte)value); + break; + case TypeCode.Byte: + WriteValueSafe((byte)value); + break; + case TypeCode.Int16: + WriteValueSafe((short)value); + break; + case TypeCode.UInt16: + WriteValueSafe((ushort)value); + break; + case TypeCode.Int32: + WriteValueSafe((int)value); + break; + case TypeCode.UInt32: + WriteValueSafe((uint)value); + break; + case TypeCode.Int64: + WriteValueSafe((long)value); + break; + case TypeCode.UInt64: + WriteValueSafe((ulong)value); + break; + } + return; + } + if (value is GameObject) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValueSafe(networkObject.NetworkObjectId); + return; + } + if (value is NetworkObject) + { + if (!((NetworkObject)value).IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); + } + + WriteValueSafe(((NetworkObject)value).NetworkObjectId); + return; + } + if (value is NetworkBehaviour) + { + if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + if (!VerifyCanWrite(sizeof(ulong) * 2)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + WriteValue(((NetworkBehaviour)value).NetworkObjectId); + WriteValue(((NetworkBehaviour)value).NetworkBehaviourId); + return; + } + if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + } + + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + public void WriteValue(T value) where T : INetworkSerializable + { + // TODO + } + + public void WriteValue(GameObject value) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValue(networkObject.NetworkObjectId); + } + + public void WriteValueSafe(GameObject value) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValueSafe(networkObject.NetworkObjectId); + } + + public static int GetWriteSize(GameObject value) + { + return sizeof(ulong); + } + + public void WriteValue(in NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + + WriteValue(value.NetworkObjectId); + } + + public void WriteValueSafe(NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + WriteValueSafe(value.NetworkObjectId); + } + + public static int GetWriteSize(NetworkObject value) + { + return sizeof(ulong); + } + + public void WriteValue(NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + WriteValue(value.NetworkObjectId); + WriteValue(value.NetworkBehaviourId); + } + + public void WriteValueSafe(NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + if (!VerifyCanWrite(sizeof(ulong) + sizeof(ushort))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + WriteValue(value.NetworkObjectId); + WriteValue(value.NetworkBehaviourId); + } + + public static int GetWriteSize(NetworkBehaviour value) + { + return sizeof(ulong) + sizeof(ushort); + } + + // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays + /// + /// Writes a string + /// + /// The string to write + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void WriteValue(string s, bool oneByteChars = false) + { + WriteValue((uint)s.Length); + int target = s.Length; + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + WriteByte((byte) s[i]); + } + } + } + else + { + fixed (char* native = s) + { + WriteBytes((byte*)native, target * sizeof(char)); + } + } + } + + // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays + /// + /// Writes a string + /// + /// The string to write + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void WriteValueSafe(string s, bool oneByteChars = false) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + int sizeInBytes = GetWriteSize(s, oneByteChars); + + if (!VerifyCanWrite(sizeInBytes)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + WriteValue((uint)s.Length); + int target = s.Length; + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + WriteByte((byte) s[i]); + } + } + } + else + { + fixed (char* native = s) + { + WriteBytes((byte*)native, target * sizeof(char)); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetWriteSize(string s, bool oneByteChars = false) + { + return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); + } + + /// + /// Writes an unmanaged array + /// + /// The array to write + /// The amount of elements to write + /// Where in the array to start + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) where T: unmanaged + { + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + WriteValue(sizeInTs); + fixed (T* native = array) + { + byte* bytes = (byte*)(native + offset); + WriteBytes(bytes, sizeInBytes); + } + } + + /// + /// Writes an unmanaged array + /// + /// The array to write + /// The amount of elements to write + /// Where in the array to start + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T: unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + + if (!VerifyCanWrite(sizeInBytes + sizeof(int))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + WriteValue(sizeInTs); + fixed (T* native = array) + { + byte* bytes = (byte*)(native + offset); + WriteBytes(bytes, sizeInBytes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged + { + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + return sizeof(int) + sizeInBytes; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + bytesToWrite > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + byte* ptr = ((byte*) &value) + offsetBytes; + byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + switch (bytesToWrite) + { + case 1: + bufferPointer[0] = *ptr; + break; + case 2: + *(ushort*) bufferPointer = *(ushort*)ptr; + break; + case 3: + *(ushort*) bufferPointer = *(ushort*)ptr; + *(bufferPointer+2) = *(ptr+2); + break; + case 4: + *(uint*) bufferPointer = *(uint*)ptr; + break; + case 5: + *(uint*) bufferPointer = *(uint*)ptr; + *(bufferPointer+4) = *(ptr+4); + break; + case 6: + *(uint*) bufferPointer = *(uint*)ptr; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + break; + case 7: + *(uint*) bufferPointer = *(uint*)ptr; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + *(bufferPointer+6) = *(ptr+6); + break; + case 8: + *(ulong*) bufferPointer = *(ulong*)ptr; + break; + default: + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); + break; + } + + m_InternalData.Position += bytesToWrite; + } + + /// + /// Write a byte to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteByte(byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + 1 > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + m_InternalData.BufferPointer[m_InternalData.Position++] = value; + } + + /// + /// Write a byte to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteByteSafe(byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanWrite(1)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + m_InternalData.BufferPointer[m_InternalData.Position++] = value; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytes(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + size > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); + m_InternalData.Position += size; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanWrite(size)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); + m_InternalData.Position += size; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytes(byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + WriteBytes(ptr, size, offset); + } + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + WriteBytesSafe(ptr, size, offset); + } + } + + /// + /// Copy the contents of this writer into another writer. + /// The contents will be copied from the beginning of this writer to its current position. + /// They will be copied to the other writer starting at the other writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyTo(FastBufferWriter other) + { + other.WriteBytes(m_InternalData.BufferPointer, m_InternalData.Position); + } + + /// + /// Copy the contents of another writer into this writer. + /// The contents will be copied from the beginning of the other writer to its current position. + /// They will be copied to this writer starting at this writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyFrom(FastBufferWriter other) + { + WriteBytes(other.m_InternalData.BufferPointer, other.m_InternalData.Position); + } + + /// + /// Write a value of any unmanaged type to the buffer. + /// It will be copied into the buffer exactly as it exists in memory. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(in T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + len > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + *pointer = value; + m_InternalData.Position += len; + } + + /// + /// Write a value of any unmanaged type to the buffer. + /// It will be copied into the buffer exactly as it exists in memory. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValueSafe(in T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanWrite(len)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + *pointer = value; + m_InternalData.Position += len; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(in T value) where T : unmanaged + { + return sizeof(T); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta new file mode 100644 index 0000000000..0c31b46524 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 819a511316a46104db673c8a0eab9e72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs new file mode 100644 index 0000000000..8ef5bdb1ed --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class SerializationTypeTable + { + public delegate void Serialize(ref FastBufferWriter writer, object value); + + public static Dictionary Serializers = new Dictionary + { + [typeof(byte)] = (ref FastBufferWriter writer, object value) => writer.WriteByteSafe((byte)value), + [typeof(sbyte)] = (ref FastBufferWriter writer, object value) => writer.WriteByteSafe((byte)(sbyte)value), + + [typeof(ushort)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ushort)value), + [typeof(short)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((short)value), + [typeof(uint)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((uint)value), + [typeof(int)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((int)value), + [typeof(ulong)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ulong)value), + [typeof(long)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((long)value), + + [typeof(float)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float)value), + [typeof(double)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double)value), + + [typeof(string)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((string)value), + + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2)value), + [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3)value), + [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4)value), + [typeof(Color)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color)value), + [typeof(Color32)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color32)value), + [typeof(Ray)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray)value), + [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D)value), + [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion)value), + + [typeof(char)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char)value), + + [typeof(bool)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool)value), + + + [typeof(byte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((byte[])value), + [typeof(sbyte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((sbyte[])value), + + [typeof(ushort[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ushort[])value), + [typeof(short[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((short[])value), + [typeof(uint[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((uint[])value), + [typeof(int[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((int[])value), + [typeof(ulong[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ulong[])value), + [typeof(long[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((long[])value), + + [typeof(float[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float[])value), + [typeof(double[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double[])value), + + [typeof(Vector2[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2[])value), + [typeof(Vector3[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3[])value), + [typeof(Vector4[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4[])value), + [typeof(Color[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color[])value), + [typeof(Color32[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color32[])value), + [typeof(Ray[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray[])value), + [typeof(Ray2D[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D[])value), + [typeof(Quaternion[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion[])value), + + [typeof(char[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char[])value), + + [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), + }; + + public static Dictionary SerializersPacked = new Dictionary + { + [typeof(byte)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (byte)value), + [typeof(sbyte)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (byte)(sbyte)value), + + [typeof(ushort)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (ushort)value), + [typeof(short)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (short)value), + [typeof(uint)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (uint)value), + [typeof(int)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (int)value), + [typeof(ulong)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (ulong)value), + [typeof(long)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (long)value), + + [typeof(float)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (float)value), + [typeof(double)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (double)value), + + [typeof(string)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (string)value), + + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector2)value), + [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector3)value), + [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector4)value), + [typeof(Color)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Color)value), + [typeof(Color32)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Color32)value), + [typeof(Ray)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray)value), + [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray2D)value), + [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Quaternion)value), + + [typeof(char)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (char)value), + + [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), + }; + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta new file mode 100644 index 0000000000..58c25d1646 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49ee6a0e2ea6e9441a74b173c31cf389 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta new file mode 100644 index 0000000000..b3c7beee0b --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e12c4be6e89f459aa2826abba8c8d301 +timeCreated: 1628799671 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs new file mode 100644 index 0000000000..12331d6ff5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using Unity.Multiplayer.Netcode; + +namespace Unity.Netcode.EditorTests +{ + public class BitCounterTests + { + [Test] + public void TestBitCounter64Bits() + { + ulong value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + + for (int i = 0; i < 64; ++i) + { + value = 1UL << i; + Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + } + } + + [Test] + public void TestBitCounter32Bits() + { + uint value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + + for (int i = 0; i < 32; ++i) + { + value = 1U << i; + Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + } + } + [Test] + public void TestByteCounter64Bits() + { + ulong value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + + for (int i = 0; i < 64; ++i) + { + value = 1UL << i; + Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + } + } + + [Test] + public void TestByteCounter32Bits() + { + uint value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + + for (int i = 0; i < 32; ++i) + { + value = 1U << i; + Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta new file mode 100644 index 0000000000..64a137560b --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76e459b9c2aeea94ebf448c237061485 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs new file mode 100644 index 0000000000..d4a41ae2ea --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -0,0 +1,178 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using Unity.Multiplayer.Netcode; + +namespace Unity.Netcode.EditorTests +{ + public class BitWriterTests + { + [Test] + public unsafe void TestWritingOneBit() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(true); + Assert.AreEqual(0b1, *asInt); + + bitWriter.WriteBit(true); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1011, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b10001011, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1010_10001011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10001011, *asInt); + + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10001011, *asInt); + } + } + + [Test] + public unsafe void TestWritingMultipleBits() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111, 1); + Assert.AreEqual(0b1, *asInt); + + bitWriter.WriteBits(0b11111111, 1); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBits(0b11111110, 2); + Assert.AreEqual(0b1011, *asInt); + + bitWriter.WriteBits(0b11111000, 4); + Assert.AreEqual(0b10001011, *asInt); + + bitWriter.WriteBits(0b11111010, 4); + Assert.AreEqual(0b1010_10001011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10001011, *asInt); + + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10001011, *asInt); + } + } + + [Test] + public unsafe void TestWritingMultipleBitsFromLongs() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111UL, 1); + Assert.AreEqual(0b1, *asInt); + + bitWriter.WriteBits(0b11111111UL, 1); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBits(0b11111110UL, 2); + Assert.AreEqual(0b1011, *asInt); + + bitWriter.WriteBits(0b11111000UL, 4); + Assert.AreEqual(0b10001011, *asInt); + + bitWriter.WriteBits(0b11111010UL, 4); + Assert.AreEqual(0b1010_10001011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10001011, *asInt); + + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10001011, *asInt); + } + } + + [Test] + public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(true); + } + }); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(false); + } + }); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111, 1); + } + }); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111UL, 1); + } + }); + + Assert.AreEqual(0, writer.Position); + Assert.AreEqual(0, *asInt); + + writer.WriteByteSafe(0b11111111); + Assert.AreEqual(0b11111111, *asInt); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta new file mode 100644 index 0000000000..3a8da0e2cc --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fed657e0516a72f469fbf886e3e5149a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs new file mode 100644 index 0000000000..4fc34219fa --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -0,0 +1,788 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.EditorTests +{ + public class BytePackerTests + { + private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) + { + + if (value <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (value <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(value) + 1, writer.Position); + } + } + + private unsafe void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(asBytes[0], value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(readValue, value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(readValue, value); + } + + private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) + { + + if (value <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (value <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(value) + 1, writer.Position); + } + } + + private unsafe void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(asBytes[0], value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(readValue, value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(readValue, value); + } + + private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) + { + ulong asUlong = Arithmetic.ZigZagEncode(value); + + if (asUlong <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (asUlong <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(asUlong) + 1, writer.Position); + } + } + + private unsafe void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + } + + private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) + { + ulong asUlong = Arithmetic.ZigZagEncode(value); + + if (asUlong <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (asUlong <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(asUlong) + 1, writer.Position); + } + } + + private unsafe void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + } + + [Test] + public void TestPacking64BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(9); + ulong value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1UL << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize64(ref writer, value); + CheckUnsignedPackedValue64(ref writer, value); + for (var j = 0; j < 8; ++j) + { + value = (1UL << i) | (1UL << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize64(ref writer, value); + CheckUnsignedPackedValue64(ref writer, value); + } + } + } + } + + [Test] + public void TestPacking32BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(9); + uint value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1U << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize32(ref writer, value); + CheckUnsignedPackedValue32(ref writer, value); + for (var j = 0; j < 8; ++j) + { + value = (1U << i) | (1U << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize32(ref writer, value); + CheckUnsignedPackedValue32(ref writer, value); + } + } + } + } + + [Test] + public void TestPacking64BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(9); + long value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1L << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize64(ref writer, value); + CheckSignedPackedValue64(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize64(ref writer, -value); + CheckSignedPackedValue64(ref writer, -value); + for (var j = 0; j < 8; ++j) + { + value = (1L << i) | (1L << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize64(ref writer, value); + CheckSignedPackedValue64(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize64(ref writer, -value); + CheckSignedPackedValue64(ref writer, -value); + } + } + } + } + + [Test] + public void TestPacking32BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(5); + int value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1 << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize32(ref writer, value); + CheckSignedPackedValue32(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize32(ref writer, -value); + CheckSignedPackedValue32(ref writer, -value); + for (var j = 0; j < 8; ++j) + { + value = (1 << i) | (1 << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize32(ref writer, value); + CheckSignedPackedValue32(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize32(ref writer, -value); + CheckSignedPackedValue32(ref writer, -value); + } + } + } + } + + private int GetByteCount61Bits(ulong value) + { + + if (value <= 0b0001_1111) + { + return 1; + } + + if (value <= 0b0001_1111_1111_1111) + { + return 2; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111) + { + return 3; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) + { + return 4; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + return 5; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + return 6; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + return 7; + } + + return 8; + } + + private int GetByteCount30Bits(uint value) + { + + if (value <= 0b0011_1111) + { + return 1; + } + + if (value <= 0b0011_1111_1111_1111) + { + return 2; + } + + if (value <= 0b0011_1111_1111_1111_1111_1111) + { + return 3; + } + + return 4; + } + + private int GetByteCount15Bits(ushort value) + { + + if (value <= 0b0111_1111) + { + return 1; + } + + return 2; + } + + private unsafe ulong Get61BitEncodedValue(byte* data) + { + ulong returnValue = 0; + byte* ptr = ((byte*) &returnValue); + int numBytes = (data[0] & 0b111) + 1; + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + case 5: + *(uint*) ptr = *(uint*)data; + *(ptr+4) = *(data+4); + break; + case 6: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + break; + case 7: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + *(ptr+6) = *(data+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)data; + break; + } + + return returnValue >> 3; + } + + private unsafe uint Get30BitEncodedValue(byte* data) + { + uint returnValue = 0; + byte* ptr = ((byte*) &returnValue); + int numBytes = (data[0] & 0b11) + 1; + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + } + + return returnValue >> 2; + } + + private unsafe ushort Get15BitEncodedValue(byte* data) + { + ushort returnValue = 0; + byte* ptr = ((byte*) &returnValue); + int numBytes = (data[0] & 0b1) + 1; + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + } + + return (ushort)(returnValue >> 1); + } + + [Test] + public unsafe void TestBitPacking61BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(8); + ulong value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b111); + Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + + for (var i = 0; i < 61; ++i) + { + value = 1UL << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + + for (var j = 0; j < 8; ++j) + { + value = (1UL << i) | (1UL << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); + } + } + + [Test] + public unsafe void TestBitPacking60BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(8); + long value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b111); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + for (var i = 0; i < 61; ++i) + { + value = 1U << i; + ulong zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + value = -value; + zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + for (var j = 0; j < 8; ++j) + { + value = (1U << i) | (1U << j); + zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + value = -value; + zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); + } + } + + [Test] + public unsafe void TestBitPacking30BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(4); + uint value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + + for (var i = 0; i < 30; ++i) + { + value = 1U << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + + for (var j = 0; j < 8; ++j) + { + value = (1U << i) | (1U << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1U << 30); }); + } + } + + [Test] + public unsafe void TestBitPacking29BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(4); + int value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + + for (var i = 0; i < 29; ++i) + { + value = 1 << i; + uint zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + + value = -value; + zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + + for (var j = 0; j < 8; ++j) + { + value = (1 << i) | (1 << j); + zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + + value = -value; + zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + } + } + } + } + + [Test] + public unsafe void TestBitPacking15BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(2); + ushort value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + + for (var i = 0; i < 15; ++i) + { + value = (ushort)(1U << i); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + + for (var j = 0; j < 8; ++j) + { + value = (ushort)((1U << i) | (1U << j)); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, (ushort)(1U << 15)); }); + } + } + [Test] + public unsafe void TestBitPacking14BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(2); + short value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + + for (var i = 0; i < 14; ++i) + { + value = (short)(1 << i); + ushort zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + + value = (short)-value; + zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + + for (var j = 0; j < 8; ++j) + { + value = (short)((1 << i) | (1 << j)); + zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + + value = (short)-value; + zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + } + } + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta new file mode 100644 index 0000000000..4c07179b82 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b50db056cd7443b4eb2e00b603d4c15c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs new file mode 100644 index 0000000000..e3daefee58 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -0,0 +1,2123 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.Internal; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode.EditorTests +{ + public class FastBufferWriterTests + { + #region Test Types + private enum ByteEnum : byte + { + A, + B, + C + }; + private enum SByteEnum : sbyte + { + A, + B, + C + }; + private enum ShortEnum : short + { + A, + B, + C + }; + private enum UShortEnum : ushort + { + A, + B, + C + }; + private enum IntEnum : int + { + A, + B, + C + }; + private enum UIntEnum : uint + { + A, + B, + C + }; + private enum LongEnum : long + { + A, + B, + C + }; + private enum ULongEnum : ulong + { + A, + B, + C + }; + + private struct TestStruct + { + public byte a; + public short b; + public ushort c; + public int d; + public uint e; + public long f; + public ulong g; + public bool h; + public char i; + public float j; + public double k; + } + + public enum WriteType + { + WriteDirect, + WriteSafe, + WriteAsObject + } + #endregion + + #region Common Checks + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + { + Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + writer.WriteValue((byte)0x80); + Assert.AreEqual(writeSize+1, writer.Position, failMessage); + Assert.AreEqual(writeSize+1, writer.Length, failMessage); + writer.WriteValue((byte)0xFF); + Assert.AreEqual(writeSize+2, writer.Position, failMessage); + Assert.AreEqual(writeSize+2, writer.Length, failMessage); + } + + private void VerifyCheckBytes(NativeArray underlyingArray, int writeSize, string failMessage = "") + { + Assert.AreEqual(0x80, underlyingArray[writeSize], failMessage); + Assert.AreEqual(0xFF, underlyingArray[writeSize+1], failMessage); + } + + private unsafe void VerifyBytewiseEquality(T value, NativeArray underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged + { + byte* asBytePointer = (byte*) &value; + for (var i = 0; i < size; ++i) + { + Assert.AreEqual(asBytePointer[i+valueOffset], underlyingArray[i+bufferOffset], failMessage); + } + } + + private unsafe void VerifyTypedEquality(T value, NativeArray underlyingArray) where T: unmanaged + { + T* checkValue = (T*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(value, *checkValue); + } + + private void VerifyPositionAndLength(ref FastBufferWriter writer, int position, string failMessage = "") + { + Assert.AreEqual(position, writer.Position, failMessage); + Assert.AreEqual(position, writer.Length, failMessage); + } + + private void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + { + NativeArray underlyingArray = writer.GetNativeArray(); + + VerifyPositionAndLength(ref writer, writeSize, failMessage); + + WriteCheckBytes(ref writer, writeSize, failMessage); + + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, writeSize, failMessage); + + VerifyCheckBytes(underlyingArray, writeSize, failMessage); + + VerifyTypedEquality(valueToTest, underlyingArray); + } + #endregion + + #region Generic Checks + private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValue(valueToTest); + + CommonChecks(ref writer, valueToTest, writeSize, failMessage); + } + } + private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValueSafe(valueToTest); + + CommonChecks(ref writer, valueToTest, writeSize, failMessage); + } + } + + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunObjectTypeTest failed with type {typeof(T)} and value {valueToTest}"; + writer.WriteObject(valueToTest); + + CommonChecks(ref writer, valueToTest, writeSize, failMessage); + } + } + + private unsafe void VerifyArrayEquality(T[] value, NativeArray underlyingArray, int offset) where T: unmanaged + { + int* sizeValue = (int*)((byte*)underlyingArray.GetUnsafePtr() + offset); + Assert.AreEqual(value.Length, *sizeValue); + + fixed (T* asTPointer = value) + { + T* underlyingTArray = (T*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + offset); + for (var i = 0; i < value.Length; ++i) + { + Assert.AreEqual(asTPointer[i], underlyingTArray[i]); + } + } + } + + private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + writer.WriteValue(valueToTest); + VerifyPositionAndLength(ref writer, writeSize); + + WriteCheckBytes(ref writer, writeSize); + + VerifyArrayEquality(valueToTest, underlyingArray, 0); + + VerifyCheckBytes(underlyingArray, writeSize); + } + } + + private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteValueSafe(valueToTest); + VerifyPositionAndLength(ref writer, writeSize); + + WriteCheckBytes(ref writer, writeSize); + + VerifyArrayEquality(valueToTest, underlyingArray, 0); + + VerifyCheckBytes(underlyingArray, writeSize); + } + } + + private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteObject(valueToTest); + VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); + + WriteCheckBytes(ref writer, writeSize + sizeof(byte)); + + VerifyArrayEquality(valueToTest, underlyingArray, sizeof(byte)); + + VerifyCheckBytes(underlyingArray, writeSize + sizeof(byte)); + } + } + #endregion + + #region Helpers + private TestStruct GetTestStruct() + { + var random = new Random(); + + var testStruct = new TestStruct + { + a = (byte) random.Next(), + b = (short) random.Next(), + c = (ushort) random.Next(), + d = (int) random.Next(), + e = (uint) random.Next(), + f = ((long) random.Next() << 32) + random.Next(), + g = ((ulong) random.Next() << 32) + (ulong) random.Next(), + h = true, + i = '\u263a', + j = (float) random.NextDouble(), + k = random.NextDouble(), + }; + + return testStruct; + } + #endregion + + #region Tests + [Test] + public void TestWritingBasicTypes( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte b = (byte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(b); + } + else + { + RunObjectTypeTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte sb = (sbyte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(sb); + } + else + { + RunObjectTypeTest(sb); + } + } + else if (testType == typeof(short)) + { + short s = (short)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(s); + } + else + { + RunObjectTypeTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort us = (ushort)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(us); + } + else + { + RunObjectTypeTest(us); + } + } + else if (testType == typeof(int)) + { + int i = (int)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(i); + } + else + { + RunObjectTypeTest(i); + } + } + else if (testType == typeof(uint)) + { + uint ui = (uint)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ui); + } + else + { + RunObjectTypeTest(ui); + } + } + else if (testType == typeof(long)) + { + long l = ((long)random.Next() << 32) + random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(l); + } + else + { + RunObjectTypeTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ul); + } + else + { + RunObjectTypeTest(ul); + } + } + else if (testType == typeof(bool)) + { + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(true); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(true); + } + else + { + RunObjectTypeTest(true); + } + } + else if (testType == typeof(char)) + { + char c = 'a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + + c = '\u263a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + } + else if (testType == typeof(float)) + { + float f = (float)random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(f); + } + else + { + RunObjectTypeTest(f); + } + } + else if (testType == typeof(double)) + { + double d = random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(d); + } + else + { + RunObjectTypeTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum e = ByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum e = SByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum e = ShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum e = UShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum e = IntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum e = UIntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum e = LongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum e = ULongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestWritingBasicArrays( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte[] b = { + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte[] sb = { + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(sb); + } + else + { + RunObjectTypeArrayTest(sb); + } + } + else if (testType == typeof(short)) + { + short[] s = { + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(s); + } + else + { + RunObjectTypeArrayTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort[] us = { + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(us); + } + else + { + RunObjectTypeArrayTest(us); + } + } + else if (testType == typeof(int)) + { + int[] i = { + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(i); + } + else + { + RunObjectTypeArrayTest(i); + } + } + else if (testType == typeof(uint)) + { + uint[] ui = { + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ui); + } + else + { + RunObjectTypeArrayTest(ui); + } + } + else if (testType == typeof(long)) + { + long[] l = { + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(l); + } + else + { + RunObjectTypeArrayTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong[] ul = { + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ul); + } + else + { + RunObjectTypeArrayTest(ul); + } + } + else if (testType == typeof(bool)) + { + bool[] b = { + true, + false, + true, + true, + false, + false, + true, + false, + true + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(char)) + { + char[] c = { + 'a', + '\u263a', + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(c); + } + else + { + RunObjectTypeArrayTest(c); + } + } + else if (testType == typeof(float)) + { + float[] f = { + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(f); + } + else + { + RunObjectTypeArrayTest(f); + } + } + else if (testType == typeof(double)) + { + double[] d = { + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(d); + } + else + { + RunObjectTypeArrayTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum[] e = { + ByteEnum.C, + ByteEnum.A, + ByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum[] e = { + SByteEnum.C, + SByteEnum.A, + SByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum[] e = { + ShortEnum.C, + ShortEnum.A, + ShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum[] e = { + UShortEnum.C, + UShortEnum.A, + UShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum[] e = { + IntEnum.C, + IntEnum.A, + IntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum[] e = { + UIntEnum.C, + UIntEnum.A, + UIntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum[] e = { + LongEnum.C, + LongEnum.A, + LongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum[] e = { + ULongEnum.C, + ULongEnum.A, + ULongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new[] + { + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new[] + { + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new[] + { + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new[] + { + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new[] + { + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new[] + { + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new[] + { + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new[] + { + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestWritingStruct() + { + RunTypeTest(GetTestStruct()); + } + + [Test] + public void TestWritingStructSafe() + { + RunTypeTestSafe(GetTestStruct()); + } + + [Test] + public void TestWritingStructAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + try + { + RunObjectTypeTest(GetTestStruct()); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public void TestWritingStructArray() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTest(arr); + } + + [Test] + public void TestWritingStructArraySafe() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTestSafe(arr); + } + + [Test] + public void TestWritingStructArrayAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + try + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunObjectTypeArrayTest(arr); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public unsafe void TestWritingString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingStringAsObject() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteObject(valueToTest); + + VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); + WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); + + int* sizeValue = (int*) ((byte*)underlyingArray.GetUnsafePtr() + sizeof(byte)); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + sizeof(byte)); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize + sizeof(byte)); + } + } + + [Test] + public unsafe void TestWritingOneByteString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, true); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingOneByteStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest, true); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count); + + var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; + VerifyPositionAndLength(ref writer, count, failMessage); + WriteCheckBytes(ref writer, count, failMessage); + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, count, failMessage); + VerifyCheckBytes(underlyingArray, count, failMessage); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); + } + } + + [Test] + public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, 2); + var failMessage = $"TestWritingPartialValuesWithOffsets failed with value {valueToTest}"; + + VerifyPositionAndLength(ref writer, count, failMessage); + WriteCheckBytes(ref writer, count, failMessage); + VerifyBytewiseEquality(valueToTest, underlyingArray, 2, 0, count, failMessage); + VerifyCheckBytes(underlyingArray, count, failMessage); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); + } + } + + [Test] + public void TestToArray() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + var array = writer.ToArray(); + var underlyingArray = writer.GetNativeArray(); + for(var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } + } + } + + [Test] + public unsafe void TestGetUnsafePtr() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + var ptr = writer.GetUnsafePtr(); + var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); + Assert.IsTrue(underlyingArrayPtr == ptr); + + var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); + Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); + } + } + + [Test] + public void TestThrowingIfBoundsCheckingSkipped() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + Assert.Throws(() => { writer.WriteByte(1); }); + var bytes = new byte[] {0, 1, 2}; + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); + int i = 1; + Assert.Throws(() => { writer.WriteValue(i); }); + Assert.Throws(() => { writer.WriteValue(bytes); }); + Assert.Throws(() => { writer.WriteValue(""); }); + + writer.VerifyCanWrite(sizeof(int) - 1); + Assert.Throws(() => { writer.WriteValue(i); }); + writer.WriteByte(1); + writer.WriteByte(2); + writer.WriteByte(3); + Assert.Throws(() => { writer.WriteByte(4); }); + } + } + + [Test] + public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(100); + var bytes = new byte[] {0, 1, 2}; + int i = 1; + using (var context = writer.EnterBitwiseContext()) + { + Assert.Throws(() => { writer.WriteByte(1); }); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); + Assert.Throws(() => { writer.WriteValue(i); }); + Assert.Throws(() => { writer.WriteValue(bytes); }); + Assert.Throws(() => { writer.WriteValue(""); }); + + Assert.Throws(() => { writer.WriteByteSafe(1); }); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); + Assert.Throws(() => { writer.WriteValueSafe(i); }); + Assert.Throws(() => { writer.WriteValueSafe(bytes); }); + Assert.Throws(() => { writer.WriteValueSafe(""); }); + } + writer.WriteByte(1); + writer.WriteBytes(bytes, bytes.Length); + writer.WriteValue(i); + writer.WriteValue(bytes); + writer.WriteValue(""); + + writer.WriteByteSafe(1); + writer.WriteBytesSafe(bytes, bytes.Length); + writer.WriteValueSafe(i); + writer.WriteValueSafe(bytes); + writer.WriteValueSafe(""); + } + } + + [Test] + public void TestVerifyCanWriteIsRelativeToPositionAndNotAllowedWritePosition() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.VerifyCanWrite(100); + writer.WriteByte(1); + writer.VerifyCanWrite(1); + writer.WriteByte(1); + Assert.Throws(() => { writer.WriteByte(1); }); + } + } + + [Test] + public void TestSeeking() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(5); + writer.WriteByteSafe(0); + Assert.AreEqual(writer.Position, 6); + Assert.AreEqual(writer.Length, 6); + + writer.Seek(0); + writer.WriteByteSafe(1); + Assert.AreEqual(writer.Position, 1); + Assert.AreEqual(writer.Length, 6); + + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + + writer.Seek(2); + writer.WriteByteSafe(2); + + writer.Seek(1); + writer.WriteByteSafe(3); + + writer.Seek(4); + writer.WriteByteSafe(4); + + writer.Seek(3); + writer.WriteByteSafe(5); + + Assert.AreEqual(writer.Position, 4); + Assert.AreEqual(writer.Length, 10); + + var expected = new byte[] {1, 3, 2, 5, 4, 0}; + var underlyingArray = writer.GetNativeArray(); + for (var i = 0; i < expected.Length; ++i) + { + Assert.AreEqual(expected[i], underlyingArray[i]); + } + } + } + + [Test] + public void TestTruncate() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + + writer.Seek(5); + Assert.AreEqual(writer.Position, 5); + Assert.AreEqual(writer.Length, 10); + + writer.Truncate(8); + Assert.AreEqual(writer.Position, 5); + Assert.AreEqual(writer.Length, 8); + + writer.Truncate(); + Assert.AreEqual(writer.Position, 5); + Assert.AreEqual(writer.Length, 5); + } + } + + [Test] + public void TestCapacity() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + Assert.AreEqual(100, writer.Capacity); + writer.Dispose(); + } + + [Test] + public void TestGrowth() + { + var writer = new FastBufferWriter(150, Allocator.Temp); + var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); + Assert.AreEqual(150, writer.Capacity); + using (writer) + using (growingWriter) + { + var testStruct = GetTestStruct(); + writer.VerifyCanWriteValue(testStruct); + writer.WriteValue(testStruct); + growingWriter.VerifyCanWriteValue(testStruct); + growingWriter.WriteValue(testStruct); + + // Seek to exactly where the write would cross the buffer boundary + writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + + // First writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); + Assert.Throws(() => writer.WriteValue(testStruct)); + + // Second writer is allowed to grow + Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); + + // First writer shouldn't have grown + Assert.AreEqual(150, writer.Capacity); + Assert.AreEqual(150, writer.GetNativeArray().Length); + // First growth doubles the size + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + growingWriter.WriteValue(testStruct); + + // Write right up to the very end of the buffer, verify it doesn't grow + growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); + Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + growingWriter.WriteValue(testStruct); + + // Go to the end of the buffer and grow again + growingWriter.Seek(300); + Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); + growingWriter.WriteValue(testStruct); + + // Second growth caps it at maxSize + Assert.AreEqual(500, growingWriter.Capacity); + Assert.AreEqual(500, growingWriter.GetNativeArray().Length); + + VerifyBytewiseEquality(testStruct, writer.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + // Verify the growth properly copied the existing data + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + } + } + + [Test] + public void TestNetworkBehavior() + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); + + writer.WriteValue(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, sizeof(ulong), sizeof(ushort)); + } + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestNetworkObject() + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); + + writer.WriteValue(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestGameObject() + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); + + writer.WriteValue(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta new file mode 100644 index 0000000000..b549311462 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1cef42b60935e29469ed1404fb30ba2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef index 35dcb1ce8d..2888eed780 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef @@ -3,12 +3,23 @@ "rootNamespace": "Unity.Netcode.EditorTests", "references": [ "Unity.Netcode.Runtime", - "Unity.Netcode.Editor" - ], - "optionalUnityReferences": [ - "TestAssemblies" + "Unity.Netcode.Editor", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" ], "includePlatforms": [ "Editor" - ] + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json b/testproject/Assets/StreamingAssets/BuildInfo.json new file mode 100644 index 0000000000..14f9fcbcd2 --- /dev/null +++ b/testproject/Assets/StreamingAssets/BuildInfo.json @@ -0,0 +1 @@ +{"BuildPath":"C:\\Users\\jaedyn.draper\\repos\\mlapi\\testproject\\Builds\\MultiprocessTests\\MultiprocessTestPlayer","IsDebug":false} \ No newline at end of file diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json.meta b/testproject/Assets/StreamingAssets/BuildInfo.json.meta new file mode 100644 index 0000000000..5af63d794e --- /dev/null +++ b/testproject/Assets/StreamingAssets/BuildInfo.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1c16680acd9943944a6d4ae05644bfd5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From c36b3af5d449ec6b421dc0cc79a5593c659594f0 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 16 Aug 2021 19:19:35 -0500 Subject: [PATCH 02/58] A few additional tests to cover the last missed cases I can think of (aside from INetworkSerializable, which isn't ready to be tested yet due to lack of BufferSerializer) --- .../Runtime/Serialization/FastBufferWriter.cs | 13 +- .../Serialization/FastBufferWriterTests.cs | 197 ++++++++++++------ 2 files changed, 139 insertions(+), 71 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index c6b88f3b60..f77d6eba14 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -295,7 +295,7 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } - WriteValueSafe(networkObject.NetworkObjectId); + WriteValueSafe(networkObject); return; } if (value is NetworkObject) @@ -305,7 +305,7 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); } - WriteValueSafe(((NetworkObject)value).NetworkObjectId); + WriteValueSafe((NetworkObject)value); return; } if (value is NetworkBehaviour) @@ -314,13 +314,8 @@ public void WriteObject(object value, bool isNullable = false) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } - - if (!VerifyCanWrite(sizeof(ulong) * 2)) - { - throw new OverflowException("Writing past the end of the buffer"); - } - WriteValue(((NetworkBehaviour)value).NetworkObjectId); - WriteValue(((NetworkBehaviour)value).NetworkBehaviourId); + + WriteValueSafe((NetworkBehaviour)value); return; } if (value is INetworkSerializable) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index e3daefee58..1685f70d66 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -254,6 +254,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + // Extra byte for WriteObject adding isNull flag FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); using(writer) { @@ -262,6 +263,7 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); + Assert.AreEqual(0, writer.GetNativeArray()[0]); VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); WriteCheckBytes(ref writer, writeSize + sizeof(byte)); @@ -1994,8 +1996,9 @@ public void TestGrowth() } } - [Test] - public void TestNetworkBehavior() + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + private void RunGameObjectTest(GameObjectTestDelegate testCode) { var obj = new GameObject("Object"); var networkBehaviour = obj.AddComponent(); @@ -2016,106 +2019,176 @@ public void TestNetworkBehavior() networkManager.StartHost(); try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestNetworkBehavior() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); - + writer.WriteValue(networkBehaviour); - + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, sizeof(ulong), sizeof(ushort)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + sizeof(ulong), sizeof(ushort)); } - } - finally - { - GameObject.DestroyImmediate(obj); - networkManager.StopHost(); - } + }); } [Test] public void TestNetworkObject() { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartHost(); - - try + RunGameObjectTest((obj, networkBehaviour, networkObject) => { var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); - + writer.WriteValue(networkObject); - + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); } - } - finally - { - GameObject.DestroyImmediate(obj); - networkManager.StopHost(); - } + }); } [Test] public void TestGameObject() { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkObject.NetworkManagerOwner = networkManager; + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() + writer.WriteValue(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + }); + } + + [Test] + public void TestNetworkBehaviorSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkBehaviour); - networkManager.StartHost(); + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + sizeof(ulong), sizeof(ushort)); + } + }); + } - try + [Test] + public void TestNetworkObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + }); + } + + [Test] + public void TestGameObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); - - writer.WriteValue(obj); - + writer.WriteValueSafe(obj); + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); } - } - finally + }); + } + + [Test] + public void TestNetworkBehaviorAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { - GameObject.DestroyImmediate(obj); - networkManager.StopHost(); - } + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); + Assert.AreEqual(0, writer.GetNativeArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + sizeof(ulong)+1, sizeof(ushort)); + } + }); + } + + [Test] + public void TestNetworkObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); + Assert.AreEqual(0, writer.GetNativeArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + } + }); + } + + [Test] + public void TestGameObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); + Assert.AreEqual(0, writer.GetNativeArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + } + }); } #endregion From 5ac5ef984308bb4c4d7d0c2affa0742ab4394470 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 18 Aug 2021 17:05:41 -0500 Subject: [PATCH 03/58] FastBufferReader + tests --- .../Runtime/Serialization/BitReader.cs | 216 ++ .../Runtime/Serialization/BitReader.cs.meta | 11 + .../Runtime/Serialization/BitWriter.cs | 55 +- .../Runtime/Serialization/BytePacker.cs | 86 +- .../Runtime/Serialization/ByteUnpacker.cs | 624 +++++ .../Serialization/ByteUnpacker.cs.meta | 11 + .../Runtime/Serialization/FastBufferReader.cs | 836 +++++++ .../Serialization/FastBufferReader.cs.meta | 11 + .../Runtime/Serialization/FastBufferWriter.cs | 152 +- .../Serialization/SerializationTypeTable.cs | 338 +++ .../Editor/Serialization/BitReaderTests.cs | 325 +++ .../Serialization/BitReaderTests.cs.meta | 11 + .../Editor/Serialization/BitWriterTests.cs | 129 + .../Editor/Serialization/BytePackerTests.cs | 320 +-- .../Serialization/FastBufferReaderTests.cs | 2137 +++++++++++++++++ .../FastBufferReaderTests.cs.meta | 11 + .../Serialization/FastBufferWriterTests.cs | 11 +- 17 files changed, 4991 insertions(+), 293 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs new file mode 100644 index 0000000000..ef383d0adf --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -0,0 +1,216 @@ +using System; +using System.Runtime.CompilerServices; +using Mono.Cecil; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct BitReader + { + private unsafe FastBufferReader.InternalData* m_InternalData; + private unsafe byte* m_BufferPointer; + private int m_Position; + private const int BITS_PER_BYTE = 8; + + + internal unsafe BitReader(ref FastBufferReader.InternalData internalData) + { + fixed (FastBufferReader.InternalData* internalDataPtr = &internalData) + { + m_InternalData = internalDataPtr; + } + + m_BufferPointer = internalData.BufferPointer + internalData.Position; + m_Position = internalData.Position; + m_InternalData->BitPosition = 0; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData->AllowedBitwiseReadMark = (m_InternalData->AllowedReadMark - m_InternalData->Position) * BITS_PER_BYTE; +#endif + } + + public unsafe void Dispose() + { + var bytesWritten = m_InternalData->BitPosition >> 3; + if (!m_InternalData->BitAligned()) + { + // Accounting for the partial read + ++bytesWritten; + } + + m_InternalData->CommitBitwiseReads(bytesWritten); + } + + /// + /// Read a certain amount of bits from the stream. + /// + /// Value to store bits into. + /// Amount of bits to read + public unsafe void ReadBits(out ulong value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (bitCount > 64) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!"); + } + + if (bitCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); + } + + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + } +#endif + ulong val = 0; + + int wholeBytes = bitCount / BITS_PER_BYTE; + byte* asBytes = (byte*) &val; + if (m_InternalData->BitAligned()) + { + if (wholeBytes != 0) + { + ReadPartialValue(out val, wholeBytes); + } + } + else + { + for (var i = 0; i < wholeBytes; ++i) + { + ReadMisaligned(out asBytes[i]); + } + } + + val |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); + value = val; + } + + /// + /// Read bits from stream. + /// + /// Value to store bits into. + /// Amount of bits to read. + public unsafe void ReadBits(out byte value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + } +#endif + value = ReadByteBits(bitCount); + } + + /// + /// Read a single bit from the buffer + /// + /// Out value of the bit. True represents 1, False represents 0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBit(out bool bit) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_InternalData->BitPosition + 1); + if (checkPos > m_InternalData->AllowedBitwiseReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + } +#endif + + int offset = m_InternalData->BitPosition & 7; + int pos = m_InternalData->BitPosition >> 3; + bit = (m_BufferPointer[pos] & (1 << offset)) != 0; + ++m_InternalData->BitPosition; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to read small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + T val = new T(); + byte* ptr = ((byte*) &val) + offsetBytes; + byte* bufferPointer = m_BufferPointer + m_Position; + switch (bytesToRead) + { + case 1: + ptr[0] = *bufferPointer; + break; + case 2: + *(ushort*) ptr = *(ushort*)bufferPointer; + break; + case 3: + *(ushort*) ptr = *(ushort*)bufferPointer; + *(ptr+2) = *(bufferPointer+2); + break; + case 4: + *(uint*) ptr = *(uint*)bufferPointer; + break; + case 5: + *(uint*) ptr = *(uint*)bufferPointer; + *(ptr+4) = *(bufferPointer+4); + break; + case 6: + *(uint*) ptr = *(uint*)bufferPointer; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + break; + case 7: + *(uint*) ptr = *(uint*)ptr; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + *(ptr+6) = *(bufferPointer+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)bufferPointer; + break; + default: + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); + break; + } + + m_InternalData->BitPosition += bytesToRead * BITS_PER_BYTE; + value = val; + } + + /// + /// Read a certain amount of bits from the stream. + /// + /// How many bits to read. Minimum 0, maximum 64. + /// The bits that were read + private byte ReadByteBits(int bitCount) + { + if (bitCount > 8) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 8 bits into an 8-bit value!"); + } + + if (bitCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); + } + + int result = 0; + var convert = new ByteBool(); + for (int i = 0; i < bitCount; ++i) + { + ReadBit(out bool bit); + result |= convert.Collapse(bit) << i; + } + + return (byte)result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void ReadMisaligned(out byte value) + { + int off = (int)(m_InternalData->BitPosition & 7); + int pos = m_InternalData->BitPosition >> 3; + int shift1 = 8 - off; + + value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_InternalData->BitPosition += 8) >> 3] << shift1)); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta new file mode 100644 index 0000000000..9b0d159683 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72e2d94a96ca96a4fb2921df9adc2fdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 8b28d3d80d..f60fabc26d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -10,13 +10,8 @@ public ref struct BitWriter private unsafe FastBufferWriter.InternalData* m_InternalData; private unsafe byte* m_BufferPointer; private int m_Position; - private int m_BitPosition; private const int BITS_PER_BYTE = 8; - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - public bool BitAligned { get => (m_BitPosition & 7) == 0; } internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) { @@ -27,13 +22,16 @@ internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) m_BufferPointer = internalData.BufferPointer + internalData.Position; m_Position = internalData.Position; - m_BitPosition = 0; + m_InternalData->BitPosition = 0; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData->AllowedBitwiseWriteMark = (m_InternalData->AllowedWriteMark - m_InternalData->Position) * BITS_PER_BYTE; +#endif } public unsafe void Dispose() { - var bytesWritten = m_BitPosition >> 3; - if (!BitAligned) + var bytesWritten = m_InternalData->BitPosition >> 3; + if (!m_InternalData->BitAligned()) { // Accounting for the partial write ++bytesWritten; @@ -60,18 +58,21 @@ public unsafe void WriteBits(ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); } - int checkPos = (m_BitPosition + bitCount) >> 3; - if (checkPos > m_InternalData->AllowedWriteMark) + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &value; - if (BitAligned) + if (m_InternalData->BitAligned()) { - WritePartialValue(value, wholeBytes); + if (wholeBytes != 0) + { + WritePartialValue(value, wholeBytes); + } } else { @@ -95,10 +96,10 @@ public unsafe void WriteBits(ulong value, int bitCount) public unsafe void WriteBits(byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + bitCount) >> 3; - if (checkPos >= m_InternalData->AllowedWriteMark) + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif @@ -112,19 +113,20 @@ public unsafe void WriteBits(byte value, int bitCount) /// Write a single bit to the buffer /// /// Value of the bit. True represents 1, False represents 0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBit(bool bit) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + 1) >> 3; - if (checkPos >= m_InternalData->AllowedWriteMark) + int checkPos = (m_InternalData->BitPosition + 1); + if (checkPos > m_InternalData->AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - int offset = m_BitPosition & 7; - int pos = m_BitPosition >> 3; - ++m_BitPosition; + int offset = m_InternalData->BitPosition & 7; + int pos = m_InternalData->BitPosition >> 3; + ++m_InternalData->BitPosition; m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); } @@ -173,18 +175,19 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy break; } - m_BitPosition += bytesToWrite * BITS_PER_BYTE; + m_InternalData->BitPosition += bytesToWrite * BITS_PER_BYTE; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WriteMisaligned(byte value) { - int off = (int)(m_BitPosition & 7); - int pos = m_BitPosition >> 3; + int off = (int)(m_InternalData->BitPosition & 7); + int pos = m_InternalData->BitPosition >> 3; int shift1 = 8 - off; m_BufferPointer[pos + 1] = (byte)((m_BufferPointer[pos + 1] & (0xFF << off)) | (value >> shift1)); m_BufferPointer[pos] = (byte)((m_BufferPointer[pos] & (0xFF >> shift1)) | (value << off)); - m_BitPosition += 8; + m_InternalData->BitPosition += 8; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index bf458eee20..f23531c73a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -138,7 +138,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteValuePacked(T value) where T: unmanaged => writer.WriteValue(value); + public void WriteValuePacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteValuePacked(ref FastBufferWriter writer, ref TEnum value) where TEnum : unmanaged, Enum @@ -189,7 +189,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByte(value); + public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. @@ -199,7 +199,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValue(value); + public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValueSafe(value); /// @@ -387,7 +387,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteValueBitPacked(T value) where T: unmanaged => writer.WriteValue(value); + public void WriteValueBitPacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); @@ -400,10 +400,18 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value if (value <= 0b0111_1111) { + if (!writer.VerifyCanWriteInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(value << 1)); return; } + if (!writer.VerifyCanWriteInternal(2)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((ushort)((value << 1) | 0b1)); } @@ -418,22 +426,38 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) if (value <= 0b0011_1111) { + if (!writer.VerifyCanWriteInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(value << 2)); return; } if (value <= 0b0011_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(2)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((ushort)((value << 2) | 0b01)); return; } if (value <= 0b0011_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(3)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue(((value << 2) | 0b10), 3); return; } + if (!writer.VerifyCanWriteInternal(4)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue(((value << 2) | 0b11)); } @@ -448,46 +472,78 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) if (value <= 0b0001_1111) { + if (!writer.VerifyCanWriteInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(value << 3)); return; } if (value <= 0b0001_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(2)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((ushort)((value << 3) | 0b001)); return; } if (value <= 0b0001_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(3)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b010, 3); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(4)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((uint)((value << 3) | 0b011)); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(5)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b100, 5); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(6)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b101, 6); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(7)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b110, 7); return; } + if (!writer.VerifyCanWriteInternal(8)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((value << 3) | 0b111); } #endif @@ -498,16 +554,21 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) { if (value <= 240) { - writer.WriteByte((byte)value); + writer.WriteByteSafe((byte)value); return; } if (value <= 2287) { - writer.WriteByte((byte)(((value - 240) >> 8) + 241)); - writer.WriteByte((byte)(value - 240)); + writer.WriteByteSafe((byte)(((value - 240) >> 8) + 241)); + writer.WriteByteSafe((byte)(value - 240)); return; } var writeBytes = BitCounter.GetUsedByteCount(value); + + if (!writer.VerifyCanWriteInternal(writeBytes+1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); } @@ -519,16 +580,21 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) { if (value <= 240) { - writer.WriteByte((byte)value); + writer.WriteByteSafe((byte)value); return; } if (value <= 2287) { - writer.WriteByte((byte)(((value - 240) >> 8) + 241)); - writer.WriteByte((byte)(value - 240)); + writer.WriteByteSafe((byte)(((value - 240) >> 8) + 241)); + writer.WriteByteSafe((byte)(value - 240)); return; } var writeBytes = BitCounter.GetUsedByteCount(value); + + if (!writer.VerifyCanWriteInternal(writeBytes+1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs new file mode 100644 index 0000000000..f63a843954 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -0,0 +1,624 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class ByteUnpacker + { + #region Managed TypePacking + /// + /// Writes a boxed object in a packed format + /// Named differently from other ReadValuePacked methods to avoid accidental boxing + /// + /// The object to write + public static void ReadObjectPacked(ref FastBufferReader reader, out object value, Type type, bool isNullable = false) + { +#if UNITY_NETCODE_DEBUG_NO_PACKING + reader.ReadObject(out value, type, isNullable); + return; +#endif + if (isNullable || type.IsNullable()) + { + reader.ReadValueSafe(out bool isNull); + + if (isNull) + { + value = null; + return; + } + } + + var hasDeserializer = SerializationTypeTable.DeserializersPacked.TryGetValue(type, out var deserializer); + if (hasDeserializer) + { + deserializer(ref reader, out value); + return; + } + + if (type.IsArray && type.HasElementType) + { + ReadValuePacked(ref reader, out int length); + + var arr = Array.CreateInstance(type.GetElementType(), length); + + for (int i = 0; i < length; i++) + { + ReadObjectPacked(ref reader, out object item, type.GetElementType()); + arr.SetValue(item, i); + } + + value = arr; + return; + } + + if (type.IsEnum) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + ReadValuePacked(ref reader, out byte boolVal); + value = Enum.ToObject(type, boolVal != 0); + return; + case TypeCode.Char: + ReadValuePacked(ref reader, out char charVal); + value = Enum.ToObject(type, charVal); + return; + case TypeCode.SByte: + ReadValuePacked(ref reader, out byte sbyteVal); + value = Enum.ToObject(type, sbyteVal); + return; + case TypeCode.Byte: + ReadValuePacked(ref reader, out byte byteVal); + value = Enum.ToObject(type, byteVal); + return; + case TypeCode.Int16: + ReadValuePacked(ref reader, out short shortVal); + value = Enum.ToObject(type, shortVal); + return; + case TypeCode.UInt16: + ReadValuePacked(ref reader, out ushort ushortVal); + value = Enum.ToObject(type, ushortVal); + return; + case TypeCode.Int32: + ReadValuePacked(ref reader, out int intVal); + value = Enum.ToObject(type, intVal); + return; + case TypeCode.UInt32: + ReadValuePacked(ref reader, out uint uintVal); + value = Enum.ToObject(type, uintVal); + return; + case TypeCode.Int64: + ReadValuePacked(ref reader, out long longVal); + value = Enum.ToObject(type, longVal); + return; + case TypeCode.UInt64: + ReadValuePacked(ref reader, out ulong ulongVal); + value = Enum.ToObject(type, ulongVal); + return; + } + } + + if (type == typeof(GameObject)) + { + reader.ReadValueSafe(out GameObject go); + value = go; + return; + } + + if (type == typeof(NetworkObject)) + { + reader.ReadValueSafe(out NetworkObject no); + value = no; + return; + } + + if (typeof(NetworkBehaviour).IsAssignableFrom(type)) + { + reader.ReadValueSafe(out NetworkBehaviour nb); + value = nb; + return; + } + /*if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + }*/ + + throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); + } + #endregion + + #region Unmanaged Type Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReadValuePacked(ref FastBufferReader reader, out T value) where T: unmanaged => reader.ReadValueSafe(out value); +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void ReadValuePacked(ref FastBufferReader reader, out TEnum value) where TEnum : unmanaged, Enum + { + switch (sizeof(TEnum)) + { + case sizeof(int): + ReadValuePacked(ref reader, out int asInt); + value = *(TEnum*)&asInt; + break; + case sizeof(byte): + ReadValuePacked(ref reader, out byte asByte); + value = *(TEnum*)&asByte; + break; + case sizeof(short): + ReadValuePacked(ref reader, out short asShort); + value = *(TEnum*)&asShort; + break; + case sizeof(long): + ReadValuePacked(ref reader, out long asLong); + value = *(TEnum*)&asLong; + break; + default: + throw new InvalidOperationException("Enum is a size that cannot exist?!"); + } + } + + /// + /// Write single-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out float value) + { + ReadUInt32Packed(ref reader, out uint asUInt); + value = ToSingle(asUInt); + } + + /// + /// Write double-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out double value) + { + ReadUInt64Packed(ref reader, out ulong asULong); + value = ToDouble(asULong); + } + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByte(out value); + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValue(out value); + + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out short value) + { + ReadUInt32Packed(ref reader, out uint readValue); + value = (short)Arithmetic.ZigZagDecode(readValue); + } + + /// + /// Write an unsigned short (UInt16) as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out ushort value) + { + ReadUInt32Packed(ref reader, out uint readValue); + value = (ushort)readValue; + } + + /// + /// Write a two-byte character as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out char c) + { + ReadUInt32Packed(ref reader, out uint readValue); + c = (char)readValue; + } + + /// + /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out int value) + { + ReadUInt32Packed(ref reader, out uint readValue); + value = (int)Arithmetic.ZigZagDecode(readValue); + } + + /// + /// Write an unsigned int (UInt32) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out uint value) => ReadUInt32Packed(ref reader, out value); + + /// + /// Write an unsigned long (UInt64) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out ulong value) => ReadUInt64Packed(ref reader, out value); + + /// + /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out long value) + { + ReadUInt64Packed(ref reader, out ulong readValue); + value = Arithmetic.ZigZagDecode(readValue); + } + + /// + /// Convenience method that writes two packed Vector3 from the ray to the stream + /// + /// Ray to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Ray ray) + { + ReadValuePacked(ref reader, out Vector3 origin); + ReadValuePacked(ref reader, out Vector3 direction); + ray = new Ray(origin, direction); + } + + /// + /// Convenience method that writes two packed Vector2 from the ray to the stream + /// + /// Ray2D to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Ray2D ray2d) + { + ReadValuePacked(ref reader, out Vector2 origin); + ReadValuePacked(ref reader, out Vector2 direction); + ray2d = new Ray2D(origin, direction); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Color color) + { + color = new Color(); + ReadValuePacked(ref reader, out color.r); + ReadValuePacked(ref reader, out color.g); + ReadValuePacked(ref reader, out color.b); + ReadValuePacked(ref reader, out color.a); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Color32 color) + { + color = new Color32(); + ReadValuePacked(ref reader, out color.r); + ReadValuePacked(ref reader, out color.g); + ReadValuePacked(ref reader, out color.b); + ReadValuePacked(ref reader, out color.a); + } + + /// + /// Convenience method that writes two varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Vector2 vector2) + { + vector2 = new Vector2(); + ReadValuePacked(ref reader, out vector2.x); + ReadValuePacked(ref reader, out vector2.y); + } + + /// + /// Convenience method that writes three varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Vector3 vector3) + { + vector3 = new Vector3(); + ReadValuePacked(ref reader, out vector3.x); + ReadValuePacked(ref reader, out vector3.y); + ReadValuePacked(ref reader, out vector3.z); + } + + /// + /// Convenience method that writes four varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Vector4 vector4) + { + vector4 = new Vector4(); + ReadValuePacked(ref reader, out vector4.x); + ReadValuePacked(ref reader, out vector4.y); + ReadValuePacked(ref reader, out vector4.z); + ReadValuePacked(ref reader, out vector4.w); + } + + /// + /// Writes the rotation to the stream. + /// + /// Rotation to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion rotation) + { + rotation = new Quaternion(); + ReadValuePacked(ref reader, out rotation.x); + ReadValuePacked(ref reader, out rotation.y); + ReadValuePacked(ref reader, out rotation.z); + + // numerical precision issues can make the remainder very slightly negative. + // In this case, use 0 for w as, otherwise, w would be NaN. + float remainder = 1f - Mathf.Pow(rotation.x, 2) - Mathf.Pow(rotation.y, 2) - Mathf.Pow(rotation.z, 2); + rotation.w = (remainder > 0f) ? Mathf.Sqrt(remainder) : 0.0f; + } + + /// + /// Writes a string in a packed format + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void ReadValuePacked(ref FastBufferReader reader, out string s) + { + ReadValuePacked(ref reader, out uint length); + s = "".PadRight((int)length); + int target = s.Length; + fixed (char* c = s) + { + for (int i = 0; i < target; ++i) + { + ReadValuePacked(ref reader, out c[i]); + } + } + } +#endif + #endregion + + #region Bit Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReadValueBitPacked(ref FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value); +#else + public static void ReadValueBitPacked(ref FastBufferReader reader, out short value) + { + ReadValueBitPacked(ref reader, out ushort readValue); + value = (short)Arithmetic.ZigZagDecode(readValue); + } + + public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ushort value) + { + ushort returnValue = 0; + byte* ptr = ((byte*) &returnValue); + byte* data = reader.GetUnsafePtrAtCurrentPosition(); + int numBytes = (data[0] & 0b1) + 1; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.MarkBytesRead(numBytes); + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + default: + throw new InvalidOperationException("Could not read bit-packed value: impossible byte count"); + } + + value = (ushort)(returnValue >> 1); + } + + public static void ReadValueBitPacked(ref FastBufferReader reader, out int value) + { + ReadValueBitPacked(ref reader, out uint readValue); + value = (int)Arithmetic.ZigZagDecode(readValue); + } + public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out uint value) + { + uint returnValue = 0; + byte* ptr = ((byte*) &returnValue); + byte* data = reader.GetUnsafePtrAtCurrentPosition(); + int numBytes = (data[0] & 0b11) + 1; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.MarkBytesRead(numBytes); + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + } + + value = returnValue >> 2; + } + + public static void ReadValueBitPacked(ref FastBufferReader reader, out long value) + { + ReadValueBitPacked(ref reader, out ulong readValue); + value = Arithmetic.ZigZagDecode(readValue); + } + public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ulong value) + { + ulong returnValue = 0; + byte* ptr = ((byte*) &returnValue); + byte* data = reader.GetUnsafePtrAtCurrentPosition(); + int numBytes = (data[0] & 0b111) + 1; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.MarkBytesRead(numBytes); + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + case 5: + *(uint*) ptr = *(uint*)data; + *(ptr+4) = *(data+4); + break; + case 6: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + break; + case 7: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + *(ptr+6) = *(data+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)data; + break; + } + + value = returnValue >> 3; + } +#endif + #endregion + + #region Private Methods + private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong value) + { + reader.ReadByteSafe(out byte firstByte); + if (firstByte <= 240) + { + value = firstByte; + return; + } + + if (firstByte <= 248) + { + reader.ReadByteSafe(out byte secondByte); + value = 240UL + ((firstByte - 241UL) << 8) + secondByte; + return; + } + + var numBytes = firstByte - 247; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.ReadPartialValue(out value, numBytes); + } + + private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value) + { + reader.ReadByteSafe(out byte firstByte); + if (firstByte <= 240) + { + value = firstByte; + return; + } + + if (firstByte <= 248) + { + reader.ReadByteSafe(out byte secondByte); + value = 240U + ((firstByte - 241U) << 8) + secondByte; + return; + } + + var numBytes = firstByte - 247; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.ReadPartialValue(out value, numBytes); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint ToUint(T value) where T : unmanaged + { + uint* asUint = (uint*) &value; + return *asUint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong ToUlong(T value) where T : unmanaged + { + ulong* asUlong = (ulong*) &value; + return *asUlong; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float ToSingle(T value) where T : unmanaged + { + float* asFloat = (float*) &value; + return *asFloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe double ToDouble(T value) where T : unmanaged + { + double* asDouble = (double*) &value; + return *asDouble; + } + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta new file mode 100644 index 0000000000..f3784bcdb4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 73484532f9cd8a7418b6a7ac770df851 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs new file mode 100644 index 0000000000..a39af22973 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -0,0 +1,836 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public struct FastBufferReader + { + private NativeArray m_buffer; + + internal struct InternalData + { + public unsafe byte* BufferPointer; + public int Position; + public int Length; + public int BitPosition; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + public int AllowedReadMark; + public int AllowedBitwiseReadMark; + public bool InBitwiseContext; +#endif + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (BitPosition & 7) == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseReads(int amount) + { + Position += amount; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + InBitwiseContext = false; +#endif + } + } + + private InternalData m_InternalData; + + public unsafe FastBufferReader(NativeArray buffer, int offset = 0, int length = -1) + { + m_buffer = buffer; + m_InternalData = new InternalData + { + BufferPointer = (byte*) m_buffer.GetUnsafePtr(), + Position = offset, + Length = length == -1 ? buffer.Length : length, +#if DEVELOPMENT_BUILD || UNITY_EDITOR + AllowedReadMark = 0, + InBitwiseContext = false, +#endif + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Seek(int where) + { + m_InternalData.Position = where; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void MarkBytesRead(int amount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + amount > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + m_InternalData.Position += amount; + } + + public unsafe BitReader EnterBitwiseContext() + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.InBitwiseContext = true; +#endif + return new BitReader(ref m_InternalData); + } + + public int Position => m_InternalData.Position; + public int Length => m_InternalData.Length; + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool VerifyCanReadInternal(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.Position + bytes > m_InternalData.AllowedReadMark) + { + m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; + } +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool VerifyCanRead(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + int len = sizeof(T); + if (m_InternalData.Position + len > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedReadMark = m_InternalData.Position + len; +#endif + return true; + } + public bool VerifyCanReadBits(int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (!m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bitwise mode while not in a bitwise context."); + } +#endif + var newBitPosition = m_InternalData.BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial read + ++totalBytesWrittenInBitwiseContext; + } + + if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedBitwiseReadMark = newBitPosition; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NativeArray GetNativeArray() + { + return m_buffer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] ToArray() + { + return m_buffer.ToArray(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtr() + { + return m_InternalData.BufferPointer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtrAtCurrentPosition() + { + return m_InternalData.BufferPointer + m_InternalData.Position; + } + + /// + /// Reads a boxed object in a standard format + /// Named differently from other ReadValue methods to avoid accidental boxing + /// + /// The object to read + /// The type to be read + /// Should a null value be encoded? Any type for which type.IsNullable() returns true will always encode it. + public void ReadObject(out object value, Type type, bool isNullable = false) + { + if (isNullable || type.IsNullable()) + { + ReadValueSafe(out bool isNull); + + if (isNull) + { + value = null; + return; + } + } + + var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); + if (hasDeserializer) + { + deserializer(ref this, out value); + return; + } + + if (type.IsArray && type.HasElementType) + { + ReadValueSafe(out int length); + + var arr = Array.CreateInstance(type.GetElementType(), length); + + for (int i = 0; i < length; i++) + { + ReadObject(out object item, type.GetElementType()); + arr.SetValue(item, i); + } + + value = arr; + return; + } + + if (type.IsEnum) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + ReadValueSafe(out byte boolVal); + value = Enum.ToObject(type, boolVal != 0); + return; + case TypeCode.Char: + ReadValueSafe(out char charVal); + value = Enum.ToObject(type, charVal); + return; + case TypeCode.SByte: + ReadValueSafe(out sbyte sbyteVal); + value = Enum.ToObject(type, sbyteVal); + return; + case TypeCode.Byte: + ReadValueSafe(out byte byteVal); + value = Enum.ToObject(type, byteVal); + return; + case TypeCode.Int16: + ReadValueSafe(out short shortVal); + value = Enum.ToObject(type, shortVal); + return; + case TypeCode.UInt16: + ReadValueSafe(out ushort ushortVal); + value = Enum.ToObject(type, ushortVal); + return; + case TypeCode.Int32: + ReadValueSafe(out int intVal); + value = Enum.ToObject(type, intVal); + return; + case TypeCode.UInt32: + ReadValueSafe(out uint uintVal); + value = Enum.ToObject(type, uintVal); + return; + case TypeCode.Int64: + ReadValueSafe(out long longVal); + value = Enum.ToObject(type, longVal); + return; + case TypeCode.UInt64: + ReadValueSafe(out ulong ulongVal); + value = Enum.ToObject(type, ulongVal); + return; + } + } + + if (type == typeof(GameObject)) + { + ReadValueSafe(out GameObject go); + value = go; + return; + } + + if (type == typeof(NetworkObject)) + { + ReadValueSafe(out NetworkObject no); + value = no; + return; + } + + if (typeof(NetworkBehaviour).IsAssignableFrom(type)) + { + ReadValueSafe(out NetworkBehaviour nb); + value = nb; + return; + } + /*if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + }*/ + + throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + /*public void ReadValue(ref T value) where T : INetworkSerializable + { + // TODO + }*/ + + public void ReadValue(out GameObject value) + { + ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValueSafe(out GameObject value) + { + ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValue(out NetworkObject value) + { + ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValueSafe(out NetworkObject value) + { + ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValue(out NetworkBehaviour value) + { + ReadValue(out ulong networkObjectId); + ReadValue(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValueSafe(out NetworkBehaviour value) + { + ReadValueSafe(out ulong networkObjectId); + ReadValueSafe(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Reads a string + /// NOTE: ALLOCATES + /// + /// Stores the read string + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void ReadValue(out string s, bool oneByteChars = false) + { + ReadValue(out uint length); + s = "".PadRight((int)length); + int target = s.Length; + fixed (char* native = s) + { + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + ReadByte(out byte b); + native[i] = (char) b; + } + } + } + else + { + ReadBytes((byte*) native, target * sizeof(char)); + } + } + } + + /// + /// Reads a string. + /// NOTE: ALLOCATES + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Stores the read string + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(sizeof(uint))) + { + throw new OverflowException("Reading past the end of the buffer"); + } + + ReadValue(out uint length); + + if (!VerifyCanReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) + { + throw new OverflowException("Reading past the end of the buffer"); + } + s = "".PadRight((int)length); + int target = s.Length; + fixed (char* native = s) + { + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + ReadByte(out byte b); + native[i] = (char) b; + } + } + } + else + { + ReadBytes((byte*) native, target * sizeof(char)); + } + } + } + + /// + /// Writes an unmanaged array + /// NOTE: ALLOCATES + /// + /// Stores the read array + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValue(out T[] array) where T: unmanaged + { + ReadValue(out int sizeInTs); + int sizeInBytes = sizeInTs * sizeof(T); + array = new T[sizeInTs]; + fixed (T* native = array) + { + byte* bytes = (byte*)(native); + ReadBytes(bytes, sizeInBytes); + } + } + + /// + /// Reads an unmanaged array + /// NOTE: ALLOCATES + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Stores the read array + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValueSafe(out T[] array) where T: unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(sizeof(int))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + ReadValue(out int sizeInTs); + int sizeInBytes = sizeInTs * sizeof(T); + if (!VerifyCanReadInternal(sizeInBytes)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + array = new T[sizeInTs]; + fixed (T* native = array) + { + byte* bytes = (byte*)(native); + ReadBytes(bytes, sizeInBytes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to read small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + bytesToRead > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + + T val = new T(); + byte* ptr = ((byte*) &val) + offsetBytes; + byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + switch (bytesToRead) + { + case 1: + ptr[0] = *bufferPointer; + break; + case 2: + *(ushort*) ptr = *(ushort*)bufferPointer; + break; + case 3: + *(ushort*) ptr = *(ushort*)bufferPointer; + *(ptr+2) = *(bufferPointer+2); + break; + case 4: + *(uint*) ptr = *(uint*)bufferPointer; + break; + case 5: + *(uint*) ptr = *(uint*)bufferPointer; + *(ptr+4) = *(bufferPointer+4); + break; + case 6: + *(uint*) ptr = *(uint*)bufferPointer; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + break; + case 7: + *(uint*) ptr = *(uint*)bufferPointer; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + *(ptr+6) = *(bufferPointer+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)bufferPointer; + break; + default: + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); + break; + } + + m_InternalData.Position += bytesToRead; + value = val; + } + + /// + /// Read a byte to the stream. + /// + /// Stores the read value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadByte(out byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + 1 > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + value = m_InternalData.BufferPointer[m_InternalData.Position++]; + } + + /// + /// Read a byte to the stream. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Stores the read value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadByteSafe(out byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + value = m_InternalData.BufferPointer[m_InternalData.Position++]; + } + + /// + /// Read multiple bytes to the stream + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytes(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + size > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); + m_InternalData.Position += size; + } + + /// + /// Read multiple bytes to the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(size)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); + m_InternalData.Position += size; + } + + /// + /// Read multiple bytes from the stream + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytes(ref byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + ReadBytes(ptr, size, offset); + } + } + + /// + /// Read multiple bytes from the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + ReadBytesSafe(ptr, size, offset); + } + } + + /// + /// Read a value of any unmanaged type to the buffer. + /// It will be copied from the buffer exactly as it existed in memory on the writing end. + /// + /// The read value + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValue(out T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + len > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + value = *pointer; + m_InternalData.Position += len; + } + + /// + /// Read a value of any unmanaged type to the buffer. + /// It will be copied from the buffer exactly as it existed in memory on the writing end. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// The read value + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValueSafe(out T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(len)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + value = *pointer; + m_InternalData.Position += len; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta new file mode 100644 index 0000000000..3667f738aa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5479786a4b1f57648a0fe56bd37a823b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index f77d6eba14..1c3e5e002f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -19,10 +19,23 @@ internal struct InternalData public int Capacity; public int MaxCapacity; public Allocator Allocator; + public int BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR public int AllowedWriteMark; + public int AllowedBitwiseWriteMark; public bool InBitwiseContext; #endif + + + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (BitPosition & 7) == 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseWrites(int amount) { @@ -154,6 +167,36 @@ public bool VerifyCanWrite(int bytes) return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool VerifyCanWriteInternal(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.Position + bytes > m_InternalData.AllowedWriteMark) + { + m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; + } +#endif + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged { @@ -181,6 +224,40 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged #endif return true; } + + public bool VerifyCanWriteBits(int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (!m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bitwise mode while not in a bitwise context."); + } +#endif + var newBitPosition = m_InternalData.BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial write + ++totalBytesWrittenInBitwiseContext; + } + + if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedBitwiseWriteMark = newBitPosition; +#endif + return true; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public NativeArray GetNativeArray() @@ -284,37 +361,16 @@ public void WriteObject(object value, bool isNullable = false) } if (value is GameObject) { - var networkObject = ((GameObject)value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - WriteValueSafe(networkObject); + WriteValueSafe((GameObject)value); return; } if (value is NetworkObject) { - if (!((NetworkObject)value).IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); - } - WriteValueSafe((NetworkObject)value); return; } if (value is NetworkBehaviour) { - if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); - } - WriteValueSafe((NetworkBehaviour)value); return; } @@ -324,7 +380,7 @@ public void WriteObject(object value, bool isNullable = false) return; } - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } public void WriteValue(T value) where T : INetworkSerializable @@ -337,12 +393,12 @@ public void WriteValue(GameObject value) var networkObject = ((GameObject)value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteValue(networkObject.NetworkObjectId); @@ -353,12 +409,12 @@ public void WriteValueSafe(GameObject value) var networkObject = ((GameObject)value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteValueSafe(networkObject.NetworkObjectId); @@ -373,7 +429,7 @@ public void WriteValue(in NetworkObject value) { if (!value.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); } WriteValue(value.NetworkObjectId); @@ -383,7 +439,7 @@ public void WriteValueSafe(NetworkObject value) { if (!value.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); } WriteValueSafe(value.NetworkObjectId); } @@ -397,7 +453,7 @@ public void WriteValue(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } WriteValue(value.NetworkObjectId); @@ -408,10 +464,10 @@ public void WriteValueSafe(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } - if (!VerifyCanWrite(sizeof(ulong) + sizeof(ushort))) + if (!VerifyCanWriteInternal(sizeof(ulong) + sizeof(ushort))) { throw new OverflowException("Writing past the end of the buffer"); } @@ -424,7 +480,6 @@ public static int GetWriteSize(NetworkBehaviour value) return sizeof(ulong) + sizeof(ushort); } - // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays /// /// Writes a string /// @@ -453,7 +508,6 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) } } - // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays /// /// Writes a string /// @@ -471,7 +525,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) int sizeInBytes = GetWriteSize(s, oneByteChars); - if (!VerifyCanWrite(sizeInBytes)) + if (!VerifyCanWriteInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -542,7 +596,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); - if (!VerifyCanWrite(sizeInBytes + sizeof(int))) + if (!VerifyCanWriteInternal(sizeInBytes + sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); } @@ -658,7 +712,7 @@ public unsafe void WriteByteSafe(byte value) } #endif - if (!VerifyCanWrite(1)) + if (!VerifyCanWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -704,7 +758,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) } #endif - if (!VerifyCanWrite(size)) + if (!VerifyCanWriteInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -811,7 +865,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged } #endif - if (!VerifyCanWrite(len)) + if (!VerifyCanWriteInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -826,5 +880,25 @@ public static unsafe int GetWriteSize(in T value) where T : unmanaged { return sizeof(T); } + + public static unsafe int GetWriteSize() where T : unmanaged + { + return sizeof(T); + } + + public static int GetNetworkObjectWriteSize() + { + return sizeof(ulong); + } + + public static int GetGameObjectWriteSize() + { + return sizeof(ulong); + } + + public static int GetNetworkBehaviourWriteSize() + { + return sizeof(ulong) + sizeof(ushort); + } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index 8ef5bdb1ed..2d714c2642 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -7,6 +7,7 @@ namespace Unity.Multiplayer.Netcode public static class SerializationTypeTable { public delegate void Serialize(ref FastBufferWriter writer, object value); + public delegate void Deserialize(ref FastBufferReader reader, out object value); public static Dictionary Serializers = new Dictionary { @@ -65,6 +66,228 @@ public static class SerializationTypeTable [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), }; + + public static Dictionary Deserializers = new Dictionary + { + [typeof(byte)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadByteSafe(out byte tmp); + value = tmp; + }, + [typeof(sbyte)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadByteSafe(out byte tmp); + value = (sbyte)tmp; + }, + + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ushort tmp); + value = tmp; + }, + [typeof(short)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out short tmp); + value = tmp; + }, + [typeof(uint)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out uint tmp); + value = tmp; + }, + [typeof(int)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out int tmp); + value = tmp; + }, + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ulong tmp); + value = tmp; + }, + [typeof(long)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out long tmp); + value = tmp; + }, + + [typeof(float)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out float tmp); + value = tmp; + }, + [typeof(double)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out double tmp); + value = tmp; + }, + + [typeof(string)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out string tmp); + value = tmp; + }, + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector2 tmp); + value = tmp; + }, + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector3 tmp); + value = tmp; + }, + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector4 tmp); + value = tmp; + }, + [typeof(Color)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color tmp); + value = tmp; + }, + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color32 tmp); + value = tmp; + }, + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray tmp); + value = tmp; + }, + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray2D tmp); + value = tmp; + }, + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Quaternion tmp); + value = tmp; + }, + + [typeof(char)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out char tmp); + value = tmp; + }, + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out bool tmp); + value = tmp; + }, + + + [typeof(byte[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out byte[] tmp); + value = tmp; + }, + [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out sbyte[] tmp); + value = tmp; + }, + + [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ushort[] tmp); + value = tmp; + }, + [typeof(short[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out short[] tmp); + value = tmp; + }, + [typeof(uint[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out uint[] tmp); + value = tmp; + }, + [typeof(int[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out int[] tmp); + value = tmp; + }, + [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ulong[] tmp); + value = tmp; + }, + [typeof(long[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out long[] tmp); + value = tmp; + }, + + [typeof(float[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out float[] tmp); + value = tmp; + }, + [typeof(double[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out double[] tmp); + value = tmp; + }, + + [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector2[] tmp); + value = tmp; + }, + [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector3[] tmp); + value = tmp; + }, + [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector4[] tmp); + value = tmp; + }, + [typeof(Color[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color[] tmp); + value = tmp; + }, + [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color32[] tmp); + value = tmp; + }, + [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray[] tmp); + value = tmp; + }, + [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray2D[] tmp); + value = tmp; + }, + [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Quaternion[] tmp); + value = tmp; + }, + + [typeof(char[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out char[] tmp); + value = tmp; + }, + + [typeof(bool[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out bool[] tmp); + value = tmp; + }, + }; public static Dictionary SerializersPacked = new Dictionary { @@ -96,5 +319,120 @@ public static class SerializationTypeTable [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), }; + + public static Dictionary DeserializersPacked = new Dictionary + { + [typeof(byte)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out byte tmp); + value = tmp; + }, + [typeof(sbyte)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out byte tmp); + value = (sbyte)tmp; + }, + + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out ushort tmp); + value = tmp; + }, + [typeof(short)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out short tmp); + value = tmp; + }, + [typeof(uint)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out uint tmp); + value = tmp; + }, + [typeof(int)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out int tmp); + value = tmp; + }, + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out ulong tmp); + value = tmp; + }, + [typeof(long)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out long tmp); + value = tmp; + }, + + [typeof(float)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out float tmp); + value = tmp; + }, + [typeof(double)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out double tmp); + value = tmp; + }, + + [typeof(string)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out string tmp); + value = tmp; + }, + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Vector2 tmp); + value = tmp; + }, + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Vector3 tmp); + value = tmp; + }, + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Vector4 tmp); + value = tmp; + }, + [typeof(Color)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Color tmp); + value = tmp; + }, + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Color32 tmp); + value = tmp; + }, + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray tmp); + value = tmp; + }, + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray2D tmp); + value = tmp; + }, + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Quaternion tmp); + value = tmp; + }, + + [typeof(char)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out char tmp); + value = tmp; + }, + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out bool tmp); + value = tmp; + }, + }; } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs new file mode 100644 index 0000000000..4034de6648 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -0,0 +1,325 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using Unity.Multiplayer.Netcode; + +namespace Unity.Netcode.EditorTests +{ + public class BitReaderTests + { + [Test] + public void TestReadingOneBit() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(true); + + bitWriter.WriteBit(true); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + } + + writer.WriteByte(0b11111111); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + bool b; + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + } + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); + } + } + [Test] + public unsafe void TestVerifyCanReadBits() + { + FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); + using (reader.GetNativeArray()) + { + int* asInt = (int*) reader.GetUnsafePtr(); + *asInt = 0b11111111_00001010_10101011; + + using (var bitReader = reader.EnterBitwiseContext()) + { + Assert.Throws(() => reader.VerifyCanRead(1)); + Assert.Throws(() => reader.VerifyCanReadValue(1)); + Assert.IsTrue(reader.VerifyCanReadBits(1)); + bitReader.ReadBit(out bool b); + Assert.IsTrue(b); + + // Can't use Assert.Throws() because ref struct BitWriter can't be captured in a lambda + try + { + bitReader.ReadBit(out b); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(reader.VerifyCanReadBits(3)); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + byte byteVal; + try + { + bitReader.ReadBits(out byteVal, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + + try + { + bitReader.ReadBits(out byteVal, 1); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(reader.VerifyCanReadBits(3)); + + try + { + bitReader.ReadBits(out byteVal, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(reader.VerifyCanReadBits(4)); + bitReader.ReadBits(out byteVal, 3); + Assert.AreEqual(0b010, byteVal); + + Assert.IsTrue(reader.VerifyCanReadBits(5)); + + bitReader.ReadBits(out byteVal, 5); + Assert.AreEqual(0b10101, byteVal); + } + + Assert.AreEqual(2, reader.Position); + + Assert.IsTrue(reader.VerifyCanRead(1)); + reader.ReadByte(out byte nextByte); + Assert.AreEqual(0b11111111, nextByte); + + Assert.IsTrue(reader.VerifyCanRead(1)); + reader.ReadByte(out nextByte); + Assert.AreEqual(0b00000000, nextByte); + + Assert.IsFalse(reader.VerifyCanRead(1)); + using (var bitReader = reader.EnterBitwiseContext()) + { + Assert.IsFalse(reader.VerifyCanReadBits(1)); + } + } + } + + [Test] + public void TestReadingMultipleBits() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111, 1); + bitWriter.WriteBits(0b11111111, 1); + bitWriter.WriteBits(0b11111110, 2); + bitWriter.WriteBits(0b11111000, 4); + bitWriter.WriteBits(0b11111010, 4); + } + writer.WriteByte(0b11111111); + + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + byte b; + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 2); + Assert.AreEqual(0b10, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1000, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1010, b); + } + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); + } + } + + [Test] + public void TestReadingMultipleBitsToLongs() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111UL, 1); + bitWriter.WriteBits(0b11111111UL, 1); + bitWriter.WriteBits(0b11111110UL, 2); + bitWriter.WriteBits(0b11111000UL, 4); + bitWriter.WriteBits(0b11111010UL, 4); + } + + writer.WriteByte(0b11111111); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + ulong ul; + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 2); + Assert.AreEqual(0b10, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1000, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1010, ul); + } + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); + } + } + + [Test] + public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() + { + FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); + using (reader.GetNativeArray()) + { + int* asInt = (int*) reader.GetUnsafePtr(); + *asInt = 0b11111111_00001010_10101011; + + Assert.Throws(() => + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBit(out bool b); + } + }); + + Assert.Throws(() => + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out byte b, 1); + } + }); + + Assert.Throws(() => + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out ulong ul, 1); + } + }); + + Assert.AreEqual(0, reader.Position); + + Assert.Throws(() => + { + Assert.IsTrue(reader.VerifyCanRead(1)); + using (var bitReader = reader.EnterBitwiseContext()) + { + ulong ul; + try + { + bitReader.ReadBits(out ul, 4); + bitReader.ReadBits(out ul, 4); + } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; + } + bitReader.ReadBits(out ul, 4); + } + }); + + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta new file mode 100644 index 0000000000..0dc0f36136 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67df11865abcd5843a4e142cf6bbd901 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index d4a41ae2ea..b03fb84d1d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -50,6 +50,114 @@ public unsafe void TestWritingOneBit() Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } } + [Test] + public unsafe void TestVerifyCanWriteBits() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + using (var bitWriter = writer.EnterBitwiseContext()) + { + Assert.Throws(() => writer.VerifyCanWrite(1)); + Assert.Throws(() => writer.VerifyCanWriteValue(1)); + Assert.IsTrue(writer.VerifyCanWriteBits(1)); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1, *asInt); + + // Can't use Assert.Throws() because ref struct BitWriter can't be captured in a lambda + try + { + bitWriter.WriteBit(true); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(writer.VerifyCanWriteBits(3)); + bitWriter.WriteBit(true); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1011, *asInt); + + try + { + bitWriter.WriteBits(0b11111111, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + + try + { + bitWriter.WriteBits(0b11111111, 1); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(writer.VerifyCanWriteBits(3)); + + try + { + bitWriter.WriteBits(0b11111111, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(writer.VerifyCanWriteBits(4)); + + bitWriter.WriteBits(0b11111010, 3); + + Assert.AreEqual(0b00101011, *asInt); + + Assert.IsTrue(writer.VerifyCanWriteBits(5)); + + bitWriter.WriteBits(0b11110101, 5); + Assert.AreEqual(0b1010_10101011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10101011, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(1)); + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10101011, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(1)); + writer.WriteByte(0b00000000); + Assert.AreEqual(0b11111111_00001010_10101011, *asInt); + + Assert.IsFalse(writer.VerifyCanWrite(1)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + Assert.IsFalse(writer.VerifyCanWriteBits(1)); + } + } + } [Test] public unsafe void TestWritingMultipleBits() @@ -172,6 +280,27 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() writer.WriteByteSafe(0b11111111); Assert.AreEqual(0b11111111, *asInt); + + + Assert.Throws(() => + { + Assert.IsTrue(writer.VerifyCanWrite(1)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + try + { + bitWriter.WriteBits(0b11111111UL, 4); + bitWriter.WriteBits(0b11111111UL, 4); + } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; + } + bitWriter.WriteBits(0b11111111UL, 1); + } + }); + } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 4fc34219fa..3a31a778b7 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -1,9 +1,7 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; -using UnityEngine; namespace Unity.Netcode.EditorTests { @@ -26,26 +24,10 @@ private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) } } - private unsafe void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) + private void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(asBytes[0], value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(readValue, value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); Assert.AreEqual(readValue, value); } @@ -66,26 +48,10 @@ private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) } } - private unsafe void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) + private void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(asBytes[0], value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(readValue, value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); Assert.AreEqual(readValue, value); } @@ -107,27 +73,11 @@ private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) } } - private unsafe void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) + private void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out long readValue); + Assert.AreEqual(readValue, value); } private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) @@ -148,27 +98,11 @@ private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) } } - private unsafe void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) + private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out int readValue); + Assert.AreEqual(readValue, value); } [Test] @@ -400,92 +334,50 @@ private int GetByteCount15Bits(ushort value) return 2; } - private unsafe ulong Get61BitEncodedValue(byte* data) + private ulong Get61BitEncodedValue(NativeArray data) { - ulong returnValue = 0; - byte* ptr = ((byte*) &returnValue); - int numBytes = (data[0] & 0b111) + 1; - switch (numBytes) - { - case 1: - *ptr = *data; - break; - case 2: - *(ushort*) ptr = *(ushort*)data; - break; - case 3: - *(ushort*) ptr = *(ushort*)data; - *(ptr+2) = *(data+2); - break; - case 4: - *(uint*) ptr = *(uint*)data; - break; - case 5: - *(uint*) ptr = *(uint*)data; - *(ptr+4) = *(data+4); - break; - case 6: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); - break; - case 7: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); - *(ptr+6) = *(data+6); - break; - case 8: - *(ulong*) ptr = *(ulong*)data; - break; - } - - return returnValue >> 3; + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out ulong value); + return value; } - private unsafe uint Get30BitEncodedValue(byte* data) + private long Get60BitSignedEncodedValue(NativeArray data) { - uint returnValue = 0; - byte* ptr = ((byte*) &returnValue); - int numBytes = (data[0] & 0b11) + 1; - switch (numBytes) - { - case 1: - *ptr = *data; - break; - case 2: - *(ushort*) ptr = *(ushort*)data; - break; - case 3: - *(ushort*) ptr = *(ushort*)data; - *(ptr+2) = *(data+2); - break; - case 4: - *(uint*) ptr = *(uint*)data; - break; - } - - return returnValue >> 2; + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out long value); + return value; } - private unsafe ushort Get15BitEncodedValue(byte* data) + private uint Get30BitEncodedValue(NativeArray data) { - ushort returnValue = 0; - byte* ptr = ((byte*) &returnValue); - int numBytes = (data[0] & 0b1) + 1; - switch (numBytes) - { - case 1: - *ptr = *data; - break; - case 2: - *(ushort*) ptr = *(ushort*)data; - break; - } + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out uint value); + return value; + } + + private int Get29BitSignedEncodedValue(NativeArray data) + { + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out int value); + return value; + } - return (ushort)(returnValue >> 1); + private ushort Get15BitEncodedValue(NativeArray data) + { + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out ushort value); + return value; + } + + private short Get14BitSignedEncodedValue(NativeArray data) + { + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out short value); + return value; } [Test] - public unsafe void TestBitPacking61BitsUnsigned() + public void TestBitPacking61BitsUnsigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -494,10 +386,10 @@ public unsafe void TestBitPacking61BitsUnsigned() writer.VerifyCanWrite(8); ulong value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b111); - Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b111); + Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); for (var i = 0; i < 61; ++i) { @@ -506,8 +398,8 @@ public unsafe void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -516,8 +408,8 @@ public unsafe void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); } } @@ -526,7 +418,7 @@ public unsafe void TestBitPacking61BitsUnsigned() } [Test] - public unsafe void TestBitPacking60BitsSigned() + public void TestBitPacking60BitsSigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -535,10 +427,10 @@ public unsafe void TestBitPacking60BitsSigned() writer.VerifyCanWrite(8); long value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b111); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(0, nativeArray[0] & 0b111); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); for (var i = 0; i < 61; ++i) { @@ -548,8 +440,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -557,8 +449,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -568,8 +460,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -577,8 +469,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); } } @@ -587,7 +479,7 @@ public unsafe void TestBitPacking60BitsSigned() } [Test] - public unsafe void TestBitPacking30BitsUnsigned() + public void TestBitPacking30BitsUnsigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -596,10 +488,10 @@ public unsafe void TestBitPacking30BitsUnsigned() writer.VerifyCanWrite(4); uint value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); for (var i = 0; i < 30; ++i) { @@ -608,8 +500,8 @@ public unsafe void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -618,8 +510,8 @@ public unsafe void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); } } @@ -628,7 +520,7 @@ public unsafe void TestBitPacking30BitsUnsigned() } [Test] - public unsafe void TestBitPacking29BitsSigned() + public void TestBitPacking29BitsSigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -637,10 +529,10 @@ public unsafe void TestBitPacking29BitsSigned() writer.VerifyCanWrite(4); int value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); for (var i = 0; i < 29; ++i) { @@ -650,8 +542,8 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -659,8 +551,8 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -670,8 +562,8 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -679,15 +571,15 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); } } } } [Test] - public unsafe void TestBitPacking15BitsUnsigned() + public void TestBitPacking15BitsUnsigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -696,10 +588,10 @@ public unsafe void TestBitPacking15BitsUnsigned() writer.VerifyCanWrite(2); ushort value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); for (var i = 0; i < 15; ++i) { @@ -708,8 +600,8 @@ public unsafe void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -718,8 +610,8 @@ public unsafe void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); } } @@ -727,7 +619,7 @@ public unsafe void TestBitPacking15BitsUnsigned() } } [Test] - public unsafe void TestBitPacking14BitsSigned() + public void TestBitPacking14BitsSigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -736,10 +628,10 @@ public unsafe void TestBitPacking14BitsSigned() writer.VerifyCanWrite(2); short value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); for (var i = 0; i < 14; ++i) { @@ -749,8 +641,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -758,8 +650,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -769,8 +661,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -778,8 +670,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs new file mode 100644 index 0000000000..97f51b5468 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -0,0 +1,2137 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.Internal; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode.EditorTests +{ + public class FastBufferReaderTests + { + #region Test Types + private enum ByteEnum : byte + { + A, + B, + C + }; + private enum SByteEnum : sbyte + { + A, + B, + C + }; + private enum ShortEnum : short + { + A, + B, + C + }; + private enum UShortEnum : ushort + { + A, + B, + C + }; + private enum IntEnum : int + { + A, + B, + C + }; + private enum UIntEnum : uint + { + A, + B, + C + }; + private enum LongEnum : long + { + A, + B, + C + }; + private enum ULongEnum : ulong + { + A, + B, + C + }; + + private struct TestStruct + { + public byte a; + public short b; + public ushort c; + public int d; + public uint e; + public long f; + public ulong g; + public bool h; + public char i; + public float j; + public double k; + } + + public enum WriteType + { + WriteDirect, + WriteSafe, + WriteAsObject + } + #endregion + + #region Common Checks + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + { + Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + writer.WriteValue((byte)0x80); + Assert.AreEqual(writeSize+1, writer.Position, failMessage); + Assert.AreEqual(writeSize+1, writer.Length, failMessage); + writer.WriteValue((byte)0xFF); + Assert.AreEqual(writeSize+2, writer.Position, failMessage); + Assert.AreEqual(writeSize+2, writer.Length, failMessage); + } + + private void VerifyCheckBytes(ref FastBufferReader reader, int checkPosition, string failMessage = "") + { + reader.Seek(checkPosition); + reader.VerifyCanRead(2); + + reader.ReadByte(out byte value); + Assert.AreEqual(0x80, value, failMessage); + reader.ReadByte(out value); + Assert.AreEqual(0xFF, value, failMessage); + } + + private void VerifyPositionAndLength(ref FastBufferReader reader, int length, string failMessage = "") + { + Assert.AreEqual(0, reader.Position, failMessage); + Assert.AreEqual(length, reader.Length, failMessage); + } + + private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + { + NativeArray underlyingArray = writer.GetNativeArray(); + + WriteCheckBytes(ref writer, writeSize, failMessage); + + FastBufferReader reader = new FastBufferReader(underlyingArray); + + VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + + VerifyCheckBytes(ref reader, writeSize, failMessage); + + reader.Seek(0); + + return reader; + } + #endregion + + #region Generic Checks + private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + Assert.AreEqual(sizeof(T), writeSize); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValue(valueToTest); + + var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); + + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); + reader.ReadValue(out T result); + Assert.AreEqual(valueToTest, result); + } + } + private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValueSafe(valueToTest); + + + var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); + + reader.ReadValueSafe(out T result); + Assert.AreEqual(valueToTest, result); + } + } + + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunObjectTypeTest failed with type {typeof(T)} and value {valueToTest}"; + writer.WriteObject(valueToTest); + + var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); + + reader.ReadObject(out object result, typeof(T)); + Assert.AreEqual(valueToTest, result); + } + } + + private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T: unmanaged + { + Assert.AreEqual(value.Length, compareValue.Length); + + for (var i = 0; i < value.Length; ++i) + { + Assert.AreEqual(value[i], compareValue[i]); + } + } + + private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + writer.WriteValue(valueToTest); + + WriteCheckBytes(ref writer, writeSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + Assert.IsTrue(reader.VerifyCanRead(writeSize)); + reader.ReadValue(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); + + VerifyCheckBytes(ref reader, writeSize); + } + } + + private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteValueSafe(valueToTest); + + WriteCheckBytes(ref writer, writeSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadValueSafe(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); + + VerifyCheckBytes(ref reader, writeSize); + } + } + + private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + // Extra byte for WriteObject adding isNull flag + FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteObject(valueToTest); + + WriteCheckBytes(ref writer, writeSize + 1); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadObject(out object result, typeof(T[])); + VerifyArrayEquality(valueToTest, (T[])result, 0); + + VerifyCheckBytes(ref reader, writeSize + 1); + } + } + #endregion + + #region Helpers + private TestStruct GetTestStruct() + { + var random = new Random(); + + var testStruct = new TestStruct + { + a = (byte) random.Next(), + b = (short) random.Next(), + c = (ushort) random.Next(), + d = (int) random.Next(), + e = (uint) random.Next(), + f = ((long) random.Next() << 32) + random.Next(), + g = ((ulong) random.Next() << 32) + (ulong) random.Next(), + h = true, + i = '\u263a', + j = (float) random.NextDouble(), + k = random.NextDouble(), + }; + + return testStruct; + } + #endregion + + #region Tests + [Test] + public void TestReadingBasicTypes( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte b = (byte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(b); + } + else + { + RunObjectTypeTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte sb = (sbyte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(sb); + } + else + { + RunObjectTypeTest(sb); + } + } + else if (testType == typeof(short)) + { + short s = (short)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(s); + } + else + { + RunObjectTypeTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort us = (ushort)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(us); + } + else + { + RunObjectTypeTest(us); + } + } + else if (testType == typeof(int)) + { + int i = (int)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(i); + } + else + { + RunObjectTypeTest(i); + } + } + else if (testType == typeof(uint)) + { + uint ui = (uint)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ui); + } + else + { + RunObjectTypeTest(ui); + } + } + else if (testType == typeof(long)) + { + long l = ((long)random.Next() << 32) + random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(l); + } + else + { + RunObjectTypeTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ul); + } + else + { + RunObjectTypeTest(ul); + } + } + else if (testType == typeof(bool)) + { + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(true); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(true); + } + else + { + RunObjectTypeTest(true); + } + } + else if (testType == typeof(char)) + { + char c = 'a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + + c = '\u263a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + } + else if (testType == typeof(float)) + { + float f = (float)random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(f); + } + else + { + RunObjectTypeTest(f); + } + } + else if (testType == typeof(double)) + { + double d = random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(d); + } + else + { + RunObjectTypeTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum e = ByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum e = SByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum e = ShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum e = UShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum e = IntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum e = UIntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum e = LongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum e = ULongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestReadingBasicArrays( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte[] b = { + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte[] sb = { + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(sb); + } + else + { + RunObjectTypeArrayTest(sb); + } + } + else if (testType == typeof(short)) + { + short[] s = { + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(s); + } + else + { + RunObjectTypeArrayTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort[] us = { + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(us); + } + else + { + RunObjectTypeArrayTest(us); + } + } + else if (testType == typeof(int)) + { + int[] i = { + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(i); + } + else + { + RunObjectTypeArrayTest(i); + } + } + else if (testType == typeof(uint)) + { + uint[] ui = { + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ui); + } + else + { + RunObjectTypeArrayTest(ui); + } + } + else if (testType == typeof(long)) + { + long[] l = { + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(l); + } + else + { + RunObjectTypeArrayTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong[] ul = { + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ul); + } + else + { + RunObjectTypeArrayTest(ul); + } + } + else if (testType == typeof(bool)) + { + bool[] b = { + true, + false, + true, + true, + false, + false, + true, + false, + true + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(char)) + { + char[] c = { + 'a', + '\u263a', + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(c); + } + else + { + RunObjectTypeArrayTest(c); + } + } + else if (testType == typeof(float)) + { + float[] f = { + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(f); + } + else + { + RunObjectTypeArrayTest(f); + } + } + else if (testType == typeof(double)) + { + double[] d = { + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(d); + } + else + { + RunObjectTypeArrayTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum[] e = { + ByteEnum.C, + ByteEnum.A, + ByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum[] e = { + SByteEnum.C, + SByteEnum.A, + SByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum[] e = { + ShortEnum.C, + ShortEnum.A, + ShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum[] e = { + UShortEnum.C, + UShortEnum.A, + UShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum[] e = { + IntEnum.C, + IntEnum.A, + IntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum[] e = { + UIntEnum.C, + UIntEnum.A, + UIntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum[] e = { + LongEnum.C, + LongEnum.A, + LongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum[] e = { + ULongEnum.C, + ULongEnum.A, + ULongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new[] + { + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new[] + { + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new[] + { + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new[] + { + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new[] + { + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new[] + { + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new[] + { + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new[] + { + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestReadingStruct() + { + RunTypeTest(GetTestStruct()); + } + + [Test] + public void TestReadingStructSafe() + { + RunTypeTestSafe(GetTestStruct()); + } + + [Test] + public void TestReadingStructAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + RunObjectTypeTest(GetTestStruct()); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public void TestReadingStructArray() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTest(arr); + } + + [Test] + public void TestReadingStructArraySafe() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTestSafe(arr); + } + + [Test] + public void TestReadingStructArrayAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunObjectTypeArrayTest(arr); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public void TestReadingString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public void TestReadingStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadValueSafe(out string result); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public void TestReadingStringAsObject() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteObject(valueToTest); + + WriteCheckBytes(ref writer, serializedValueSize+1); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadObject(out object result, typeof(string)); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize+1); + } + } + + [Test] + public void TestReadingOneByteString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, true); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result, true); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public void TestReadingOneByteStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest, true); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadValueSafe(out string result, true); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count); + + var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; + WriteCheckBytes(ref writer, count, failMessage); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + reader.ReadPartialValue(out ulong result, count); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); + } + } + + [Test] + public unsafe void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, 2); + var failMessage = $"TestReadingPartialValuesWithOffsets failed with value {valueToTest}"; + WriteCheckBytes(ref writer, count, failMessage); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + mask <<= 16; + + reader.ReadPartialValue(out ulong result, count, 2); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); + } + } + + [Test] + public void TestToArray() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + + var reader = new FastBufferReader(writer.GetNativeArray()); + var array = reader.ToArray(); + var underlyingArray = writer.GetNativeArray(); + for(var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } + } + } + + [Test] + public unsafe void TestGetUnsafePtr() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + var reader = new FastBufferReader(writer.GetNativeArray()); + var ptr = reader.GetUnsafePtr(); + var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); + Assert.IsTrue(underlyingArrayPtr == ptr); + + var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); + Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); + } + } + + [Test] + public void TestThrowingIfBoundsCheckingSkipped() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + var emptyReader = new FastBufferReader(new NativeArray(100, Allocator.Temp), 0, 0); + + using (emptyReader.GetNativeArray()) + using (writer) + { + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); + var bytes = new byte[] {0, 1, 2}; + Assert.Throws(() => { emptyReader.ReadBytes(ref bytes, bytes.Length); }); + int i = 1; + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); + Assert.Throws(() => { emptyReader.ReadValue(out bytes); }); + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); + + writer.VerifyCanWrite(sizeof(int) - 1); + writer.WriteByte(1); + writer.WriteByte(2); + writer.WriteByte(3); + var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); + Assert.Throws(() => { reader.ReadValue(out int i); }); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + Assert.IsTrue(reader.VerifyCanRead(3)); + reader.ReadByte(out byte b); + reader.ReadByte(out b); + reader.ReadByte(out b); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + } + } + + [Test] + public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(100); + var bytes = new byte[] {0, 1, 2}; + int i = 1; + writer.WriteByte(1); + writer.WriteBytes(bytes, bytes.Length); + writer.WriteValue(i); + writer.WriteValue(bytes); + writer.WriteValue(""); + + writer.WriteByteSafe(1); + writer.WriteBytesSafe(bytes, bytes.Length); + writer.WriteValueSafe(i); + writer.WriteValueSafe(bytes); + writer.WriteValueSafe(""); + + var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); + Assert.IsTrue(reader.VerifyCanRead(writer.Length)); + using (var context = reader.EnterBitwiseContext()) + { + Assert.Throws(() => { reader.ReadByte(out byte b); }); + Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); + Assert.Throws(() => { reader.ReadValue(out i); }); + Assert.Throws(() => { reader.ReadValue(out bytes); }); + Assert.Throws(() => { reader.ReadValue(out string s); }); + + Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); + Assert.Throws(() => { reader.ReadBytesSafe(ref bytes, bytes.Length); }); + Assert.Throws(() => { reader.ReadValueSafe(out i); }); + Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); + Assert.Throws(() => { reader.ReadValueSafe(out string s); }); + } + } + } + + [Test] + public void TestVerifyCanReadIsRelativeToPositionAndNotAllowedReadPosition() + { + var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); + using (reader.GetNativeArray()) + { + reader.VerifyCanRead(100); + reader.ReadByte(out byte b); + reader.VerifyCanRead(1); + reader.ReadByte(out b); + Assert.Throws(() => { reader.ReadByte(out b); }); + } + } + + [Test] + public void TestSeeking() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.WriteByteSafe(1); + writer.WriteByteSafe(3); + writer.WriteByteSafe(2); + writer.WriteByteSafe(5); + writer.WriteByteSafe(4); + writer.WriteByteSafe(0); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.Seek(5); + reader.ReadByteSafe(out byte b); + Assert.AreEqual(reader.Position, 6); + Assert.AreEqual(reader.Length, 100); + Assert.AreEqual(0, b); + + reader.Seek(0); + reader.ReadByteSafe(out b); + Assert.AreEqual(reader.Position, 1); + Assert.AreEqual(reader.Length, 100); + Assert.AreEqual(1, b); + + reader.Seek(10); + Assert.AreEqual(reader.Position, 10); + Assert.AreEqual(reader.Length, 100); + + reader.Seek(2); + reader.ReadByteSafe(out b); + Assert.AreEqual(2, b); + + reader.Seek(1); + reader.ReadByteSafe(out b); + Assert.AreEqual(3, b); + + reader.Seek(4); + reader.ReadByteSafe(out b); + Assert.AreEqual(4, b); + + reader.Seek(3); + reader.ReadByteSafe(out b); + Assert.AreEqual(5, b); + + Assert.AreEqual(reader.Position, 4); + Assert.AreEqual(reader.Length, 100); + } + } + + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + private void RunGameObjectTest(GameObjectTestDelegate testCode) + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkManager.SetSingleton(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestNetworkBehaviour() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); + + writer.WriteValue(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); + reader.ReadValue(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + + } + }); + } + + [Test] + public void TestNetworkObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); + + writer.WriteValue(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); + reader.ReadValue(out NetworkObject result); + Assert.AreSame(result, networkObject); + } + }); + } + + [Test] + public void TestGameObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); + + writer.WriteValue(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); + reader.ReadValue(out GameObject result); + Assert.AreSame(result, obj); + } + }); + } + + [Test] + public void TestNetworkBehaviourSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadValueSafe(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + } + }); + } + + [Test] + public void TestNetworkObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadValueSafe(out NetworkObject result); + Assert.AreSame(result, networkObject); + } + }); + } + + [Test] + public void TestGameObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadValueSafe(out GameObject result); + Assert.AreSame(result, obj); + } + }); + } + + [Test] + public void TestNetworkBehaviourAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadObject(out object result, typeof(NetworkBehaviour)); + Assert.AreSame(result, networkBehaviour); + } + }); + } + + [Test] + public void TestNetworkObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadObject(out object result, typeof(NetworkObject)); + Assert.AreSame(result, networkObject); + } + }); + } + + [Test] + public void TestGameObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadObject(out object result, typeof(GameObject)); + Assert.AreSame(result, obj); + } + }); + } + + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta new file mode 100644 index 0000000000..52af796039 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2881f8138b479c34389b76687e5307ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 1685f70d66..d1776840ed 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -145,11 +145,14 @@ private void CommonChecks(ref FastBufferWriter writer, T valueToTest, int wri private unsafe void RunTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + var alternateWriteSize = FastBufferWriter.GetWriteSize(); + Assert.AreEqual(sizeof(T), writeSize); + Assert.AreEqual(sizeof(T), alternateWriteSize); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using(writer) { - Assert.AreEqual(sizeof(T), writeSize); Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -2030,7 +2033,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } [Test] - public void TestNetworkBehavior() + public void TestNetworkBehaviour() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { @@ -2086,7 +2089,7 @@ public void TestGameObject() } [Test] - public void TestNetworkBehaviorSafe() + public void TestNetworkBehaviourSafe() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { @@ -2136,7 +2139,7 @@ public void TestGameObjectSafe() } [Test] - public void TestNetworkBehaviorAsObject() + public void TestNetworkBehaviourAsObject() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { From 47767371ca5592548d8847144d3fc6ba4bc370a7 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 19 Aug 2021 13:18:17 -0500 Subject: [PATCH 04/58] - More tests - Streamlined the implementations of bit-packing uints and ulongs. --- .../Runtime/Serialization/BytePacker.cs | 160 ++--- .../Runtime/Serialization/ByteUnpacker.cs | 26 +- .../Runtime/Serialization/FastBufferReader.cs | 2 +- .../Runtime/Serialization/FastBufferWriter.cs | 2 +- .../Editor/Serialization/BytePackerTests.cs | 573 +++++++++++++++++- .../Serialization/FastBufferReaderTests.cs | 12 + .../Serialization/FastBufferWriterTests.cs | 11 + 7 files changed, 644 insertions(+), 142 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index f23531c73a..3976032578 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -141,7 +141,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, public void WriteValuePacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteValuePacked(ref FastBufferWriter writer, ref TEnum value) where TEnum : unmanaged, Enum + public static unsafe void WriteValuePacked(ref FastBufferWriter writer, TEnum value) where TEnum : unmanaged, Enum { TEnum enumValue = value; switch (sizeof(TEnum)) @@ -191,6 +191,16 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, sbyte value) => writer.WriteByteSafe((byte)value); + /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. /// WARNING: If the value you're writing is > 2287, this will use MORE space @@ -351,18 +361,10 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rotation) { - if (Mathf.Sign(rotation.w) < 0) - { - WriteValuePacked(ref writer, -rotation.x); - WriteValuePacked(ref writer, -rotation.y); - WriteValuePacked(ref writer, -rotation.z); - } - else - { - WriteValuePacked(ref writer, rotation.x); - WriteValuePacked(ref writer, rotation.y); - WriteValuePacked(ref writer, rotation.z); - } + WriteValuePacked(ref writer, rotation.x); + WriteValuePacked(ref writer, rotation.y); + WriteValuePacked(ref writer, rotation.z); + WriteValuePacked(ref writer, rotation.w); } /// @@ -393,16 +395,18 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value) { +#if DEVELOPMENT_BUILD || UNITY_EDITOR if (value >= 0b1000_0000_0000_0000) { throw new ArgumentException("BitPacked ushorts must be <= 15 bits"); } +#endif if (value <= 0b0111_1111) { if (!writer.VerifyCanWriteInternal(1)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(value << 1)); return; @@ -410,7 +414,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value if (!writer.VerifyCanWriteInternal(2)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteValue((ushort)((value << 1) | 0b1)); } @@ -419,132 +423,38 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) { +#if DEVELOPMENT_BUILD || UNITY_EDITOR if (value >= 0b0100_0000_0000_0000_0000_0000_0000_0000) { throw new ArgumentException("BitPacked uints must be <= 30 bits"); } - - if (value <= 0b0011_1111) - { - if (!writer.VerifyCanWriteInternal(1)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteByte((byte)(value << 2)); - return; - } - - if (value <= 0b0011_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(2)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteValue((ushort)((value << 2) | 0b01)); - return; - } - - if (value <= 0b0011_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(3)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue(((value << 2) | 0b10), 3); - return; - } - - if (!writer.VerifyCanWriteInternal(4)) +#endif + value <<= 2; + var numBytes = BitCounter.GetUsedByteCount(value); + if (!writer.VerifyCanWriteInternal(numBytes)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } - writer.WriteValue(((value << 2) | 0b11)); + writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, (ulong) Arithmetic.ZigZagEncode(value)); public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) { +#if DEVELOPMENT_BUILD || UNITY_EDITOR if (value >= 0b0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000) { throw new ArgumentException("BitPacked ulongs must be <= 61 bits"); } - - if (value <= 0b0001_1111) - { - if (!writer.VerifyCanWriteInternal(1)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteByte((byte)(value << 3)); - return; - } - - if (value <= 0b0001_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(2)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteValue((ushort)((value << 3) | 0b001)); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(3)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b010, 3); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(4)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteValue((uint)((value << 3) | 0b011)); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(5)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b100, 5); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(6)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b101, 6); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(7)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b110, 7); - return; - } - - if (!writer.VerifyCanWriteInternal(8)) +#endif + value <<= 3; + var numBytes = BitCounter.GetUsedByteCount(value); + if (!writer.VerifyCanWriteInternal(numBytes)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } - writer.WriteValue((value << 3) | 0b111); + writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } #endif #endregion @@ -567,7 +477,7 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) if (!writer.VerifyCanWriteInternal(writeBytes+1)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); @@ -593,7 +503,7 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) if (!writer.VerifyCanWriteInternal(writeBytes+1)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index f63a843954..5ac013acec 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -193,8 +193,22 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByte(out value); - + public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByteSafe(out value); + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) + { + reader.ReadByteSafe(out byte byteVal); + value = (sbyte) byteVal; + } + /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. /// WARNING: If the value you're writing is > 2287, this will use MORE space @@ -203,7 +217,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValue(out value); + public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValueSafe(out value); /// @@ -386,11 +400,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion r ReadValuePacked(ref reader, out rotation.x); ReadValuePacked(ref reader, out rotation.y); ReadValuePacked(ref reader, out rotation.z); - - // numerical precision issues can make the remainder very slightly negative. - // In this case, use 0 for w as, otherwise, w would be NaN. - float remainder = 1f - Mathf.Pow(rotation.x, 2) - Mathf.Pow(rotation.y, 2) - Mathf.Pow(rotation.z, 2); - rotation.w = (remainder > 0f) ? Mathf.Sqrt(remainder) : 0.0f; + ReadValuePacked(ref reader, out rotation.w); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index a39af22973..be07e4980b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -42,7 +42,7 @@ internal void CommitBitwiseReads(int amount) } } - private InternalData m_InternalData; + internal InternalData m_InternalData; public unsafe FastBufferReader(NativeArray buffer, int offset = 0, int length = -1) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 1c3e5e002f..0658676296 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -46,7 +46,7 @@ internal void CommitBitwiseWrites(int amount) } } - private InternalData m_InternalData; + internal InternalData m_InternalData; public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 3a31a778b7..d11744c2b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -1,15 +1,100 @@ using System; +using System.Linq; +using System.Reflection; using NUnit.Framework; using Unity.Collections; using Unity.Multiplayer.Netcode; +using UnityEngine; +using Random = System.Random; namespace Unity.Netcode.EditorTests { public class BytePackerTests { + #region Test Types + + private enum ByteEnum : byte + { + A, + B, + C + } + + private enum SByteEnum : sbyte + { + A, + B, + C + } + + private enum ShortEnum : short + { + A, + B, + C + } + + private enum UShortEnum : ushort + { + A, + B, + C + } + + private enum IntEnum + { + A, + B, + C + } + + private enum UIntEnum : uint + { + A, + B, + C + } + + private enum LongEnum : long + { + A, + B, + C + } + + private enum ULongEnum : ulong + { + A, + B, + C + } + + private struct TestStruct + { + public byte a; + public short b; + public ushort c; + public int d; + public uint e; + public long f; + public ulong g; + public bool h; + public char i; + public float j; + public double k; + } + + public enum WriteType + { + WriteDirect, + WriteAsObject + } + + #endregion + private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) { - + if (value <= 240) { Assert.AreEqual(1, writer.Position); @@ -30,10 +115,10 @@ private void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); Assert.AreEqual(readValue, value); } - + private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) { - + if (value <= 240) { Assert.AreEqual(1, writer.Position); @@ -54,11 +139,11 @@ private void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); Assert.AreEqual(readValue, value); } - + private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) { ulong asUlong = Arithmetic.ZigZagEncode(value); - + if (asUlong <= 240) { Assert.AreEqual(1, writer.Position); @@ -79,11 +164,11 @@ private void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) ByteUnpacker.ReadValuePacked(ref reader, out long readValue); Assert.AreEqual(readValue, value); } - + private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) { ulong asUlong = Arithmetic.ZigZagEncode(value); - + if (asUlong <= 240) { Assert.AreEqual(1, writer.Position); @@ -105,6 +190,59 @@ private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) Assert.AreEqual(readValue, value); } + private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: unmanaged + { + byte* asBytePointer = (byte*) &value; + byte* otherBytePointer = (byte*) &otherValue; + for (var i = 0; i < sizeof(T); ++i) + { + Assert.AreEqual(asBytePointer[i], otherBytePointer[i]); + } + } + + private unsafe void RunTypeTest(T value) where T : unmanaged + { + FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteValuePacked(ref writer, (dynamic)value); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + + T outVal = new T(); + MethodInfo method; + if (value is Enum) + { + method = typeof(ByteUnpacker).GetMethods().Single(x => x.Name == "ReadValuePacked" && x.IsGenericMethodDefinition) + .MakeGenericMethod(typeof(T)); + } + else + { + method = typeof (ByteUnpacker).GetMethod("ReadValuePacked", new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + } + object[] args = {reader, outVal}; + method.Invoke(null, args); + outVal = (T) args[1]; + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, outVal); + } + } + + private unsafe void RunObjectTypeTest(T value) where T : unmanaged + { + FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteObjectPacked(ref writer, value); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, (T)outVal); + } + } + + + [Test] public void TestPacking64BitsUnsigned() { @@ -676,5 +814,426 @@ public void TestBitPacking14BitsSigned() } } } + + [Test] + public void TestPackingBasicTypes( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte b = (byte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(b); + } + else + { + RunObjectTypeTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte sb = (sbyte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(sb); + } + else + { + RunObjectTypeTest(sb); + } + } + else if (testType == typeof(short)) + { + short s = (short)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(s); + } + else + { + RunObjectTypeTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort us = (ushort)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(us); + } + else + { + RunObjectTypeTest(us); + } + } + else if (testType == typeof(int)) + { + int i = random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(i); + } + else + { + RunObjectTypeTest(i); + } + } + else if (testType == typeof(uint)) + { + uint ui = (uint)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ui); + } + else + { + RunObjectTypeTest(ui); + } + } + else if (testType == typeof(long)) + { + long l = ((long)random.Next() << 32) + random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(l); + } + else + { + RunObjectTypeTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ul); + } + else + { + RunObjectTypeTest(ul); + } + } + else if (testType == typeof(bool)) + { + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(true); + } + else + { + RunObjectTypeTest(true); + } + } + else if (testType == typeof(char)) + { + char c = 'a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else + { + RunObjectTypeTest(c); + } + + c = '\u263a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else + { + RunObjectTypeTest(c); + } + } + else if (testType == typeof(float)) + { + float f = (float)random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(f); + } + else + { + RunObjectTypeTest(f); + } + } + else if (testType == typeof(double)) + { + double d = random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(d); + } + else + { + RunObjectTypeTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum e = ByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum e = SByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum e = ShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum e = UShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum e = IntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum e = UIntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum e = LongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum e = ULongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray)) + { + // Rays need special handling on the equality checks because the constructor normalizes direction + // Which can cause slight variations in the result + var v = new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteValuePacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out Ray outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + Assert.AreEqual(v.direction.z, outVal.direction.z, 0.00001); + } + } + } + else + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteObjectPacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); + Assert.AreEqual(v.origin, ((Ray)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); + Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); + } + } + } + } + else if (testType == typeof(Ray2D)) + { + // Rays need special handling on the equality checks because the constructor normalizes direction + // Which can cause slight variations in the result + var v = new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteValuePacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out Ray2D outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + } + } + } + else + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteObjectPacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); + Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); + } + } + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 97f51b5468..49e423d04f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -2132,6 +2132,18 @@ public void TestGameObjectAsObject() }); } + [Test] + public void TestVerifyInternalDoesntReduceAllowedWritePoint() + { + var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); + using (reader.GetNativeArray()) + { + reader.VerifyCanRead(25); + reader.VerifyCanReadInternal(5); + Assert.AreEqual(reader.m_InternalData.AllowedReadMark, 25); + } + } + #endregion } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index d1776840ed..2291627605 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -2194,6 +2194,17 @@ public void TestGameObjectAsObject() }); } + [Test] + public void TestVerifyInternalDoesntReduceAllowedWritePoint() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.VerifyCanWrite(25); + writer.VerifyCanWriteInternal(5); + Assert.AreEqual(writer.m_InternalData.AllowedWriteMark, 25); + } + } #endregion } } \ No newline at end of file From f24515097f994ce69a8ce1395b697d25d4fa3400 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 19 Aug 2021 17:13:27 -0500 Subject: [PATCH 05/58] - Removed NativeArray from FastBufferReader and FastBufferWriter, replacing with byte* allocated directly through UnsafeUtility.Malloc() (same function used under the hood by NativeArray). This is required in order to be able to store pointers to FastBufferReader and FastBufferWriter in order to be able to wrap them with BufferSerializer - NativeArray contains a managed variable in it that disallows taking a pointer to it. And since FBW and FBR are structs, pointers are the only way to wrap them "by reference" in another struct - ref fields aren't allowed even inside ref structs. --- .../Runtime/Serialization/BitReader.cs | 82 ++- .../Runtime/Serialization/BitWriter.cs | 94 +++- .../Runtime/Serialization/FastBufferReader.cs | 268 +++++----- .../Runtime/Serialization/FastBufferWriter.cs | 262 ++++------ .../Editor/Serialization/BitReaderTests.cs | 180 ++++--- .../Editor/Serialization/BitWriterTests.cs | 12 +- .../Editor/Serialization/BytePackerTests.cs | 309 ++++++----- .../Serialization/FastBufferReaderTests.cs | 490 ++++++++++-------- .../Serialization/FastBufferWriterTests.cs | 148 +++--- 9 files changed, 978 insertions(+), 867 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index ef383d0adf..a194e228ad 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -8,37 +8,69 @@ namespace Unity.Multiplayer.Netcode { public ref struct BitReader { - private unsafe FastBufferReader.InternalData* m_InternalData; + private unsafe FastBufferReader* m_Reader; private unsafe byte* m_BufferPointer; private int m_Position; private const int BITS_PER_BYTE = 8; + private int m_BitPosition; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + private int m_AllowedBitwiseReadMark; +#endif + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (m_BitPosition & 7) == 0; + } - internal unsafe BitReader(ref FastBufferReader.InternalData internalData) + internal unsafe BitReader(ref FastBufferReader reader) { - fixed (FastBufferReader.InternalData* internalDataPtr = &internalData) + fixed (FastBufferReader* readerPtr = &reader) { - m_InternalData = internalDataPtr; + m_Reader = readerPtr; } - m_BufferPointer = internalData.BufferPointer + internalData.Position; - m_Position = internalData.Position; - m_InternalData->BitPosition = 0; + m_BufferPointer = m_Reader->m_BufferPointer + m_Reader->Position; + m_Position = m_Reader->Position; + m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData->AllowedBitwiseReadMark = (m_InternalData->AllowedReadMark - m_InternalData->Position) * BITS_PER_BYTE; + m_AllowedBitwiseReadMark = (m_Reader->m_AllowedReadMark - m_Position) * BITS_PER_BYTE; #endif } public unsafe void Dispose() { - var bytesWritten = m_InternalData->BitPosition >> 3; - if (!m_InternalData->BitAligned()) + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned()) { // Accounting for the partial read ++bytesWritten; } - m_InternalData->CommitBitwiseReads(bytesWritten); + m_Reader->CommitBitwiseReads(bytesWritten); + } + + public unsafe bool VerifyCanReadBits(int bitCount) + { + var newBitPosition = m_BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial read + ++totalBytesWrittenInBitwiseContext; + } + + if (m_Reader->m_Position + totalBytesWrittenInBitwiseContext > m_Reader->m_Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedBitwiseReadMark = newBitPosition; +#endif + return true; } /// @@ -59,8 +91,8 @@ public unsafe void ReadBits(out ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); } - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseReadMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } @@ -69,7 +101,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &val; - if (m_InternalData->BitAligned()) + if (BitAligned()) { if (wholeBytes != 0) { @@ -96,8 +128,8 @@ public unsafe void ReadBits(out ulong value, int bitCount) public unsafe void ReadBits(out byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseReadMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } @@ -113,17 +145,17 @@ public unsafe void ReadBits(out byte value, int bitCount) public unsafe void ReadBit(out bool bit) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + 1); - if (checkPos > m_InternalData->AllowedBitwiseReadMark) + int checkPos = (m_BitPosition + 1); + if (checkPos > m_AllowedBitwiseReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } #endif - int offset = m_InternalData->BitPosition & 7; - int pos = m_InternalData->BitPosition >> 3; + int offset = m_BitPosition & 7; + int pos = m_BitPosition >> 3; bit = (m_BufferPointer[pos] & (1 << offset)) != 0; - ++m_InternalData->BitPosition; + ++m_BitPosition; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -171,7 +203,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB break; } - m_InternalData->BitPosition += bytesToRead * BITS_PER_BYTE; + m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; } @@ -206,11 +238,11 @@ private byte ReadByteBits(int bitCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void ReadMisaligned(out byte value) { - int off = (int)(m_InternalData->BitPosition & 7); - int pos = m_InternalData->BitPosition >> 3; + int off = (int)(m_BitPosition & 7); + int pos = m_BitPosition >> 3; int shift1 = 8 - off; - value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_InternalData->BitPosition += 8) >> 3] << shift1)); + value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_BitPosition += 8) >> 3] << shift1)); } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index f60fabc26d..b941d36f39 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,43 +1,83 @@ using System; using System.Runtime.CompilerServices; -using Mono.Cecil; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { public ref struct BitWriter { - private unsafe FastBufferWriter.InternalData* m_InternalData; + private unsafe FastBufferWriter* m_Writer; private unsafe byte* m_BufferPointer; private int m_Position; + internal int m_BitPosition; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + internal int m_AllowedBitwiseWriteMark; +#endif private const int BITS_PER_BYTE = 8; + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (m_BitPosition & 7) == 0; + } + - internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) + internal unsafe BitWriter(ref FastBufferWriter writer) { - fixed (FastBufferWriter.InternalData* internalDataPtr = &internalData) + fixed (FastBufferWriter* internalDataPtr = &writer) { - m_InternalData = internalDataPtr; + m_Writer = internalDataPtr; } - m_BufferPointer = internalData.BufferPointer + internalData.Position; - m_Position = internalData.Position; - m_InternalData->BitPosition = 0; + m_BufferPointer = writer.m_BufferPointer + writer.m_Position; + m_Position = writer.m_Position; + m_BitPosition = 0; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedBitwiseWriteMark = (m_Writer->m_AllowedWriteMark - m_Writer->Position) * BITS_PER_BYTE; +#endif + } + + public unsafe bool VerifyCanWriteBits(int bitCount) + { + var newBitPosition = m_BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial write + ++totalBytesWrittenInBitwiseContext; + } + + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer->m_Capacity) + { + if (m_Writer->m_Capacity < m_Writer->m_MaxCapacity) + { + m_Writer->Grow(); + m_BufferPointer = m_Writer->m_BufferPointer; + } + else + { + return false; + } + } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData->AllowedBitwiseWriteMark = (m_InternalData->AllowedWriteMark - m_InternalData->Position) * BITS_PER_BYTE; + m_AllowedBitwiseWriteMark = newBitPosition; #endif + return true; } public unsafe void Dispose() { - var bytesWritten = m_InternalData->BitPosition >> 3; - if (!m_InternalData->BitAligned()) + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned()) { // Accounting for the partial write ++bytesWritten; } - m_InternalData->CommitBitwiseWrites(bytesWritten); + m_Writer->CommitBitwiseWrites(bytesWritten); } /// @@ -58,8 +98,8 @@ public unsafe void WriteBits(ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); } - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseWriteMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseWriteMark) { throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } @@ -67,7 +107,7 @@ public unsafe void WriteBits(ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &value; - if (m_InternalData->BitAligned()) + if (BitAligned()) { if (wholeBytes != 0) { @@ -93,11 +133,11 @@ public unsafe void WriteBits(ulong value, int bitCount) /// /// Value to get bits from. /// Amount of bits to write. - public unsafe void WriteBits(byte value, int bitCount) + public void WriteBits(byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseWriteMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseWriteMark) { throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } @@ -117,16 +157,16 @@ public unsafe void WriteBits(byte value, int bitCount) public unsafe void WriteBit(bool bit) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + 1); - if (checkPos > m_InternalData->AllowedBitwiseWriteMark) + int checkPos = (m_BitPosition + 1); + if (checkPos > m_AllowedBitwiseWriteMark) { throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - int offset = m_InternalData->BitPosition & 7; - int pos = m_InternalData->BitPosition >> 3; - ++m_InternalData->BitPosition; + int offset = m_BitPosition & 7; + int pos = m_BitPosition >> 3; + ++m_BitPosition; m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); } @@ -175,19 +215,19 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy break; } - m_InternalData->BitPosition += bytesToWrite * BITS_PER_BYTE; + m_BitPosition += bytesToWrite * BITS_PER_BYTE; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WriteMisaligned(byte value) { - int off = (int)(m_InternalData->BitPosition & 7); - int pos = m_InternalData->BitPosition >> 3; + int off = m_BitPosition & 7; + int pos = m_BitPosition >> 3; int shift1 = 8 - off; m_BufferPointer[pos + 1] = (byte)((m_BufferPointer[pos + 1] & (0xFF << off)) | (value >> shift1)); m_BufferPointer[pos] = (byte)((m_BufferPointer[pos] & (0xFF >> shift1)) | (value << off)); - m_InternalData->BitPosition += 8; + m_BitPosition += 8; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index be07e4980b..a46f6967a1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.CompilerServices; -using System.Text; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; @@ -8,110 +7,160 @@ namespace Unity.Multiplayer.Netcode { - public struct FastBufferReader + public struct FastBufferReader : IDisposable { - private NativeArray m_buffer; + internal unsafe byte* m_BufferPointer; + internal int m_Position; + internal int m_Length; + internal Allocator m_Allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + internal int m_AllowedReadMark; + internal bool m_InBitwiseContext; +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseReads(int amount) + { + m_Position += amount; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InBitwiseContext = false; +#endif + } - internal struct InternalData + public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, int length = -1, int offset = 0) { - public unsafe byte* BufferPointer; - public int Position; - public int Length; - public int BitPosition; + m_Length = Math.Max(1, length == -1 ? buffer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr()+offset, m_Length); + m_BufferPointer = (byte*)bufferPtr; + m_Position = offset; + m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - public int AllowedReadMark; - public int AllowedBitwiseReadMark; - public bool InBitwiseContext; + m_AllowedReadMark = 0; + m_InBitwiseContext = false; #endif - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() + } + + public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) + { + m_Length = Math.Max(1, length == -1 ? buffer.Count : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer.Array) { - return (BitPosition & 7) == 0; + UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CommitBitwiseReads(int amount) - { - Position += amount; + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - InBitwiseContext = false; + m_AllowedReadMark = 0; + m_InBitwiseContext = false; #endif - } } - - internal InternalData m_InternalData; - - public unsafe FastBufferReader(NativeArray buffer, int offset = 0, int length = -1) + + public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { - m_buffer = buffer; - m_InternalData = new InternalData + m_Length = Math.Max(1, length == -1 ? buffer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer) { - BufferPointer = (byte*) m_buffer.GetUnsafePtr(), - Position = offset, - Length = length == -1 ? buffer.Length : length, + UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); + } + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - AllowedReadMark = 0, - InBitwiseContext = false, + m_AllowedReadMark = 0; + m_InBitwiseContext = false; #endif - }; + } + + public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) + { + m_Length = Math.Max(1, length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, buffer + offset, m_Length); + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedReadMark = 0; + m_InBitwiseContext = false; +#endif + } + + public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0) + { + m_Length = Math.Max(1, length == -1 ? writer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, writer.GetUnsafePtr() + offset, m_Length); + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedReadMark = 0; + m_InBitwiseContext = false; +#endif + } + + public unsafe void Dispose() + { + UnsafeUtility.Free(m_BufferPointer, m_Allocator); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { - m_InternalData.Position = where; + m_Position = Math.Min(Length, where); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void MarkBytesRead(int amount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + amount > m_InternalData.AllowedReadMark) + if (m_Position + amount > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - m_InternalData.Position += amount; + m_Position += amount; } - public unsafe BitReader EnterBitwiseContext() + public BitReader EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.InBitwiseContext = true; + m_InBitwiseContext = true; #endif - return new BitReader(ref m_InternalData); + return new BitReader(ref this); } - public int Position => m_InternalData.Position; - public int Length => m_InternalData.Length; + public int Position => m_Position; + public int Length => m_Length; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool VerifyCanReadInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Length) + if (m_Position + bytes > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.Position + bytes > m_InternalData.AllowedReadMark) + if (m_Position + bytes > m_AllowedReadMark) { - m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; + m_AllowedReadMark = m_Position + bytes; } #endif return true; @@ -121,18 +170,18 @@ internal bool VerifyCanReadInternal(int bytes) public bool VerifyCanRead(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Length) + if (m_Position + bytes > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; + m_AllowedReadMark = m_Position + bytes; #endif return true; } @@ -141,71 +190,44 @@ public bool VerifyCanRead(int bytes) public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif int len = sizeof(T); - if (m_InternalData.Position + len > m_InternalData.Length) - { - return false; - } -#if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedReadMark = m_InternalData.Position + len; -#endif - return true; - } - public bool VerifyCanReadBits(int bitCount) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (!m_InternalData.InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferReader in bitwise mode while not in a bitwise context."); - } -#endif - var newBitPosition = m_InternalData.BitPosition + bitCount; - var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; - if ((newBitPosition & 7) != 0) - { - // Accounting for the partial read - ++totalBytesWrittenInBitwiseContext; - } - - if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Length) + if (m_Position + len > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedBitwiseReadMark = newBitPosition; + m_AllowedReadMark = m_Position + len; #endif return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NativeArray GetNativeArray() + public unsafe byte[] ToArray() { - return m_buffer; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte[] ToArray() - { - return m_buffer.ToArray(); + byte[] ret = new byte[Length]; + fixed (byte* b = ret) + { + UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + } + return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_InternalData.BufferPointer; + return m_BufferPointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_InternalData.BufferPointer + m_InternalData.Position; + return m_BufferPointer + m_Position; } /// @@ -459,11 +481,8 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - ReadByte(out byte b); - native[i] = (char) b; - } + ReadByte(out byte b); + native[i] = (char) b; } } else @@ -485,7 +504,7 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -511,11 +530,8 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - ReadByte(out byte b); - native[i] = (char) b; - } + ReadByte(out byte b); + native[i] = (char) b; } } else @@ -555,7 +571,7 @@ public unsafe void ReadValue(out T[] array) where T: unmanaged public unsafe void ReadValueSafe(out T[] array) where T: unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -588,12 +604,12 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB // in all builds - editor, mono, and ILCPP #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + bytesToRead > m_InternalData.AllowedReadMark) + if (m_Position + bytesToRead > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } @@ -601,7 +617,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; - byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + byte* bufferPointer = m_BufferPointer + m_Position; switch (bytesToRead) { case 1: @@ -638,7 +654,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB break; } - m_InternalData.Position += bytesToRead; + m_Position += bytesToRead; value = val; } @@ -650,17 +666,17 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB public unsafe void ReadByte(out byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + 1 > m_InternalData.AllowedReadMark) + if (m_Position + 1 > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - value = m_InternalData.BufferPointer[m_InternalData.Position++]; + value = m_BufferPointer[m_Position++]; } /// @@ -674,7 +690,7 @@ public unsafe void ReadByte(out byte value) public unsafe void ReadByteSafe(out byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -685,7 +701,7 @@ public unsafe void ReadByteSafe(out byte value) { throw new OverflowException("Reading past the end of the buffer"); } - value = m_InternalData.BufferPointer[m_InternalData.Position++]; + value = m_BufferPointer[m_Position++]; } /// @@ -698,18 +714,18 @@ public unsafe void ReadByteSafe(out byte value) public unsafe void ReadBytes(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + size > m_InternalData.AllowedReadMark) + if (m_Position + size > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); + m_Position += size; } /// @@ -725,7 +741,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -736,8 +752,8 @@ public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); + m_Position += size; } /// @@ -785,20 +801,20 @@ public unsafe void ReadValue(out T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + len > m_InternalData.AllowedReadMark) + if (m_Position + len > m_AllowedReadMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); value = *pointer; - m_InternalData.Position += len; + m_Position += len; } /// @@ -816,7 +832,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -828,9 +844,9 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged throw new OverflowException("Writing past the end of the buffer"); } - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); value = *pointer; - m_InternalData.Position += len; + m_Position += len; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 0658676296..6d3df83614 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -9,71 +9,48 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferWriter : IDisposable { - private NativeArray m_buffer; - - internal struct InternalData - { - public unsafe byte* BufferPointer; - public int Position; - public int Length; - public int Capacity; - public int MaxCapacity; - public Allocator Allocator; - public int BitPosition; + internal unsafe byte* m_BufferPointer; + internal int m_Position; + internal int m_Length; + internal int m_Capacity; + internal int m_MaxCapacity; + internal Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - public int AllowedWriteMark; - public int AllowedBitwiseWriteMark; - public bool InBitwiseContext; + internal int m_AllowedWriteMark; + internal bool m_InBitwiseContext; #endif - - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() - { - return (BitPosition & 7) == 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CommitBitwiseWrites(int amount) - { - Position += amount; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseWrites(int amount) + { + m_Position += amount; #if DEVELOPMENT_BUILD || UNITY_EDITOR - InBitwiseContext = false; + m_InBitwiseContext = false; #endif - } } - internal InternalData m_InternalData; - public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) { + void* buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR - var buffer = new NativeArray(size, allocator, NativeArrayOptions.ClearMemory); -#else - var buffer = new NativeArray(size, allocator, NativeArrayOptions.UninitializedMemory); + UnsafeUtility.MemSet(buffer, 0, size); #endif - m_buffer = buffer; - m_InternalData = new InternalData - { - BufferPointer = (byte*) m_buffer.GetUnsafePtr(), - Position = 0, - Length = 0, - Capacity = buffer.Length, - Allocator = allocator, - MaxCapacity = maxSize == -1 ? size : maxSize, + m_BufferPointer = (byte*)buffer; + m_Position = 0; + m_Length = 0; + m_Capacity = size; + m_Allocator = allocator; + m_MaxCapacity = maxSize == -1 ? size : maxSize; #if DEVELOPMENT_BUILD || UNITY_EDITOR - AllowedWriteMark = 0, - InBitwiseContext = false, + m_AllowedWriteMark = 0; + m_InBitwiseContext = false; #endif - }; } - public void Dispose() + public unsafe void Dispose() { - m_buffer.Dispose(); + UnsafeUtility.Free(m_BufferPointer, m_Allocator); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -89,11 +66,12 @@ public void Seek(int where) // because position increases, and if we seek backward, length remembers // the position it was in. // Seeking forward will not update the length. - if (m_InternalData.Position > m_InternalData.Length && where < m_InternalData.Position) + where = Math.Min(where, m_Capacity); + if (m_Position > m_Length && where < m_Position) { - m_InternalData.Length = m_InternalData.Position; + m_Length = m_Position; } - m_InternalData.Position = where; + m_Position = where; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -104,55 +82,54 @@ public void Truncate(int where = -1) where = Position; } - if (m_InternalData.Position > where) + if (m_Position > where) { - m_InternalData.Position = where; + m_Position = where; } - if(m_InternalData.Length > where) + if(m_Length > where) { - m_InternalData.Length = where; + m_Length = where; } } - public unsafe BitWriter EnterBitwiseContext() + public BitWriter EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.InBitwiseContext = true; + m_InBitwiseContext = true; #endif - return new BitWriter(ref m_InternalData); + return new BitWriter(ref this); } - public int Position => m_InternalData.Position; - public int Capacity => m_InternalData.Capacity; - public int Length => m_InternalData.Position > m_InternalData.Length ? m_InternalData.Position : m_InternalData.Length; + public int Position => m_Position; + public int Capacity => m_Capacity; + public int Length => m_Position > m_Length ? m_Position : m_Length; - private unsafe void Grow() + internal unsafe void Grow() { + var newSize = Math.Min(m_Capacity * 2, m_MaxCapacity); + void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR - var buffer = new NativeArray(Math.Min(m_InternalData.Capacity * 2, m_InternalData.MaxCapacity), m_InternalData.Allocator, NativeArrayOptions.ClearMemory); -#else - var buffer = new NativeArray(m_InternalData.Capacity * 2, m_InternalData.Allocator, NativeArrayOptions.UninitializedMemory); + UnsafeUtility.MemSet(buffer, 0, newSize); #endif - UnsafeUtility.MemCpy(buffer.GetUnsafePtr(), m_InternalData.BufferPointer, Length); - m_buffer.Dispose(); - m_buffer = buffer; - m_InternalData.BufferPointer = (byte*) m_buffer.GetUnsafePtr(); - m_InternalData.Capacity = buffer.Length; + UnsafeUtility.MemCpy(buffer, m_BufferPointer, Length); + UnsafeUtility.Free(m_BufferPointer, m_Allocator); + m_BufferPointer = (byte*)buffer; + m_Capacity = newSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool VerifyCanWrite(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Capacity) + if (m_Position + bytes > m_Capacity) { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + if (m_Capacity < m_MaxCapacity) { Grow(); } @@ -162,7 +139,7 @@ public bool VerifyCanWrite(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; + m_AllowedWriteMark = m_Position + bytes; #endif return true; } @@ -171,15 +148,15 @@ public bool VerifyCanWrite(int bytes) public bool VerifyCanWriteInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Capacity) + if (m_Position + bytes > m_Capacity) { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + if (m_Capacity < m_MaxCapacity) { Grow(); } @@ -189,9 +166,9 @@ public bool VerifyCanWriteInternal(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.Position + bytes > m_InternalData.AllowedWriteMark) + if (m_Position + bytes > m_AllowedWriteMark) { - m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; + m_AllowedWriteMark = m_Position + bytes; } #endif return true; @@ -201,16 +178,16 @@ public bool VerifyCanWriteInternal(int bytes) public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif int len = sizeof(T); - if (m_InternalData.Position + len > m_InternalData.Capacity) + if (m_Position + len > m_Capacity) { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + if (m_Capacity < m_MaxCapacity) { Grow(); } @@ -220,67 +197,32 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedWriteMark = m_InternalData.Position + len; + m_AllowedWriteMark = m_Position + len; #endif return true; } - - public bool VerifyCanWriteBits(int bitCount) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (!m_InternalData.InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferWriter in bitwise mode while not in a bitwise context."); - } -#endif - var newBitPosition = m_InternalData.BitPosition + bitCount; - var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; - if ((newBitPosition & 7) != 0) - { - // Accounting for the partial write - ++totalBytesWrittenInBitwiseContext; - } - - if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Capacity) - { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) - { - Grow(); - } - else - { - return false; - } - } -#if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedBitwiseWriteMark = newBitPosition; -#endif - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NativeArray GetNativeArray() - { - return m_buffer; - } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte[] ToArray() + public unsafe byte[] ToArray() { - return m_buffer.ToArray(); + byte[] ret = new byte[Length]; + fixed (byte* b = ret) + { + UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + } + return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_InternalData.BufferPointer; + return m_BufferPointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_InternalData.BufferPointer + m_InternalData.Position; + return m_BufferPointer + m_Position; } /// @@ -493,10 +435,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - WriteByte((byte) s[i]); - } + WriteByte((byte) s[i]); } } else @@ -516,7 +455,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) public unsafe void WriteValueSafe(string s, bool oneByteChars = false) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -536,10 +475,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - WriteByte((byte) s[i]); - } + WriteByte((byte) s[i]); } } else @@ -586,7 +522,7 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T: unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -624,19 +560,19 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt // in all builds - editor, mono, and ILCPP #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + bytesToWrite > m_InternalData.AllowedWriteMark) + if (m_Position + bytesToWrite > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif byte* ptr = ((byte*) &value) + offsetBytes; - byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + byte* bufferPointer = m_BufferPointer + m_Position; switch (bytesToWrite) { case 1: @@ -673,7 +609,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt break; } - m_InternalData.Position += bytesToWrite; + m_Position += bytesToWrite; } /// @@ -684,17 +620,17 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt public unsafe void WriteByte(byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + 1 > m_InternalData.AllowedWriteMark) + if (m_Position + 1 > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - m_InternalData.BufferPointer[m_InternalData.Position++] = value; + m_BufferPointer[m_Position++] = value; } /// @@ -705,7 +641,7 @@ public unsafe void WriteByte(byte value) public unsafe void WriteByteSafe(byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -716,7 +652,7 @@ public unsafe void WriteByteSafe(byte value) { throw new OverflowException("Writing past the end of the buffer"); } - m_InternalData.BufferPointer[m_InternalData.Position++] = value; + m_BufferPointer[m_Position++] = value; } /// @@ -728,18 +664,18 @@ public unsafe void WriteByteSafe(byte value) public unsafe void WriteBytes(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + size > m_InternalData.AllowedWriteMark) + if (m_Position + size > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); + m_Position += size; } /// @@ -751,7 +687,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -762,8 +698,8 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); + m_Position += size; } /// @@ -803,7 +739,7 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyTo(FastBufferWriter other) { - other.WriteBytes(m_InternalData.BufferPointer, m_InternalData.Position); + other.WriteBytes(m_BufferPointer, m_Position); } /// @@ -815,7 +751,7 @@ public unsafe void CopyTo(FastBufferWriter other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyFrom(FastBufferWriter other) { - WriteBytes(other.m_InternalData.BufferPointer, other.m_InternalData.Position); + WriteBytes(other.m_BufferPointer, other.m_Position); } /// @@ -830,20 +766,20 @@ public unsafe void WriteValue(in T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + len > m_InternalData.AllowedWriteMark) + if (m_Position + len > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); *pointer = value; - m_InternalData.Position += len; + m_Position += len; } /// @@ -858,7 +794,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -870,9 +806,9 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged throw new OverflowException("Writing past the end of the buffer"); } - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); *pointer = value; - m_InternalData.Position += len; + m_Position += len; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index 4034de6648..a395fa43b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -36,47 +36,53 @@ public void TestReadingOneBit() writer.WriteByte(0b11111111); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(3)); - using (var bitReader = reader.EnterBitwiseContext()) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - bool b; - bitReader.ReadBit(out b); - Assert.IsTrue(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + bool b; + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + } + + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); } - reader.ReadByte(out byte lastByte); - Assert.AreEqual(0b11111111, lastByte); } } [Test] public unsafe void TestVerifyCanReadBits() { - FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); - using (reader.GetNativeArray()) + var nativeArray = new NativeArray(4, Allocator.Temp); + FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + nativeArray.Dispose(); + using (reader) { int* asInt = (int*) reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; @@ -85,7 +91,7 @@ public unsafe void TestVerifyCanReadBits() { Assert.Throws(() => reader.VerifyCanRead(1)); Assert.Throws(() => reader.VerifyCanReadValue(1)); - Assert.IsTrue(reader.VerifyCanReadBits(1)); + Assert.IsTrue(bitReader.VerifyCanReadBits(1)); bitReader.ReadBit(out bool b); Assert.IsTrue(b); @@ -102,7 +108,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(reader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.VerifyCanReadBits(3)); bitReader.ReadBit(out b); Assert.IsTrue(b); bitReader.ReadBit(out b); @@ -136,7 +142,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(reader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.VerifyCanReadBits(3)); try { @@ -150,11 +156,11 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(reader.VerifyCanReadBits(4)); + Assert.IsTrue(bitReader.VerifyCanReadBits(4)); bitReader.ReadBits(out byteVal, 3); Assert.AreEqual(0b010, byteVal); - Assert.IsTrue(reader.VerifyCanReadBits(5)); + Assert.IsTrue(bitReader.VerifyCanReadBits(5)); bitReader.ReadBits(out byteVal, 5); Assert.AreEqual(0b10101, byteVal); @@ -173,7 +179,7 @@ public unsafe void TestVerifyCanReadBits() Assert.IsFalse(reader.VerifyCanRead(1)); using (var bitReader = reader.EnterBitwiseContext()) { - Assert.IsFalse(reader.VerifyCanReadBits(1)); + Assert.IsFalse(bitReader.VerifyCanReadBits(1)); } } } @@ -196,28 +202,32 @@ public void TestReadingMultipleBits() writer.WriteByte(0b11111111); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(3)); - using (var bitReader = reader.EnterBitwiseContext()) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - byte b; - bitReader.ReadBits(out b, 1); - Assert.AreEqual(0b1, b); - - bitReader.ReadBits(out b, 1); - Assert.AreEqual(0b1, b); - - bitReader.ReadBits(out b, 2); - Assert.AreEqual(0b10, b); - - bitReader.ReadBits(out b, 4); - Assert.AreEqual(0b1000, b); - - bitReader.ReadBits(out b, 4); - Assert.AreEqual(0b1010, b); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + byte b; + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 2); + Assert.AreEqual(0b10, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1000, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1010, b); + } + + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); } - reader.ReadByte(out byte lastByte); - Assert.AreEqual(0b11111111, lastByte); } } @@ -239,36 +249,42 @@ public void TestReadingMultipleBitsToLongs() writer.WriteByte(0b11111111); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(3)); - using (var bitReader = reader.EnterBitwiseContext()) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - ulong ul; - bitReader.ReadBits(out ul, 1); - Assert.AreEqual(0b1, ul); - - bitReader.ReadBits(out ul, 1); - Assert.AreEqual(0b1, ul); - - bitReader.ReadBits(out ul, 2); - Assert.AreEqual(0b10, ul); - - bitReader.ReadBits(out ul, 4); - Assert.AreEqual(0b1000, ul); - - bitReader.ReadBits(out ul, 4); - Assert.AreEqual(0b1010, ul); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + ulong ul; + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 2); + Assert.AreEqual(0b10, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1000, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1010, ul); + } + + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); } - reader.ReadByte(out byte lastByte); - Assert.AreEqual(0b11111111, lastByte); } } [Test] public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() { - FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); - using (reader.GetNativeArray()) + var nativeArray = new NativeArray(4, Allocator.Temp); + FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + nativeArray.Dispose(); + using (reader) { int* asInt = (int*) reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index b03fb84d1d..7614242e93 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -64,7 +64,7 @@ public unsafe void TestVerifyCanWriteBits() { Assert.Throws(() => writer.VerifyCanWrite(1)); Assert.Throws(() => writer.VerifyCanWriteValue(1)); - Assert.IsTrue(writer.VerifyCanWriteBits(1)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(1)); bitWriter.WriteBit(true); Assert.AreEqual(0b1, *asInt); @@ -81,7 +81,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(writer.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); @@ -114,7 +114,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(writer.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); try { @@ -128,13 +128,13 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(writer.VerifyCanWriteBits(4)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(4)); bitWriter.WriteBits(0b11111010, 3); Assert.AreEqual(0b00101011, *asInt); - Assert.IsTrue(writer.VerifyCanWriteBits(5)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(5)); bitWriter.WriteBits(0b11110101, 5); Assert.AreEqual(0b1010_10101011, *asInt); @@ -154,7 +154,7 @@ public unsafe void TestVerifyCanWriteBits() Assert.IsFalse(writer.VerifyCanWrite(1)); using (var bitWriter = writer.EnterBitwiseContext()) { - Assert.IsFalse(writer.VerifyCanWriteBits(1)); + Assert.IsFalse(bitWriter.VerifyCanWriteBits(1)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index d11744c2b0..9ae500584c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -111,9 +111,12 @@ private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) private void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); + Assert.AreEqual(readValue, value); + } } private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) @@ -135,9 +138,12 @@ private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) private void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); + Assert.AreEqual(readValue, value); + } } private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) @@ -160,9 +166,12 @@ private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) private void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out long readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out long readValue); + Assert.AreEqual(readValue, value); + } } private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) @@ -185,9 +194,12 @@ private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out int readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out int readValue); + Assert.AreEqual(readValue, value); + } } private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: unmanaged @@ -206,24 +218,30 @@ private unsafe void RunTypeTest(T value) where T : unmanaged using (writer) { BytePacker.WriteValuePacked(ref writer, (dynamic)value); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - - T outVal = new T(); - MethodInfo method; - if (value is Enum) - { - method = typeof(ByteUnpacker).GetMethods().Single(x => x.Name == "ReadValuePacked" && x.IsGenericMethodDefinition) - .MakeGenericMethod(typeof(T)); - } - else + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - method = typeof (ByteUnpacker).GetMethod("ReadValuePacked", new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + + T outVal = new T(); + MethodInfo method; + if (value is Enum) + { + method = typeof(ByteUnpacker).GetMethods().Single(x => + x.Name == "ReadValuePacked" && x.IsGenericMethodDefinition) + .MakeGenericMethod(typeof(T)); + } + else + { + method = typeof(ByteUnpacker).GetMethod("ReadValuePacked", + new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + } + + object[] args = {reader, outVal}; + method.Invoke(null, args); + outVal = (T) args[1]; + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, outVal); } - object[] args = {reader, outVal}; - method.Invoke(null, args); - outVal = (T) args[1]; - Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, outVal); } } @@ -233,11 +251,14 @@ private unsafe void RunObjectTypeTest(T value) where T : unmanaged using (writer) { BytePacker.WriteObjectPacked(ref writer, value); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); - Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, (T)outVal); + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, (T) outVal); + } } } @@ -472,46 +493,64 @@ private int GetByteCount15Bits(ushort value) return 2; } - private ulong Get61BitEncodedValue(NativeArray data) + private ulong Get61BitEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out ulong value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out ulong value); + return value; + } } - private long Get60BitSignedEncodedValue(NativeArray data) + private long Get60BitSignedEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out long value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out long value); + return value; + } } - private uint Get30BitEncodedValue(NativeArray data) + private uint Get30BitEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out uint value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out uint value); + return value; + } } - private int Get29BitSignedEncodedValue(NativeArray data) + private int Get29BitSignedEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out int value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out int value); + return value; + } } - private ushort Get15BitEncodedValue(NativeArray data) + private ushort Get15BitEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out ushort value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out ushort value); + return value; + } } - private short Get14BitSignedEncodedValue(NativeArray data) + private short Get14BitSignedEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out short value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out short value); + return value; + } } [Test] @@ -524,10 +563,9 @@ public void TestBitPacking61BitsUnsigned() writer.VerifyCanWrite(8); ulong value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b111); - Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b111); + Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); for (var i = 0; i < 61; ++i) { @@ -536,8 +574,8 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -546,8 +584,8 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); } } @@ -565,10 +603,9 @@ public void TestBitPacking60BitsSigned() writer.VerifyCanWrite(8); long value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b111); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b111); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); for (var i = 0; i < 61; ++i) { @@ -578,8 +615,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -587,8 +624,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -598,8 +635,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -607,8 +644,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); } } @@ -626,10 +663,9 @@ public void TestBitPacking30BitsUnsigned() writer.VerifyCanWrite(4); uint value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); for (var i = 0; i < 30; ++i) { @@ -638,8 +674,8 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -648,8 +684,8 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); } } @@ -667,10 +703,9 @@ public void TestBitPacking29BitsSigned() writer.VerifyCanWrite(4); int value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); for (var i = 0; i < 29; ++i) { @@ -680,8 +715,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -689,8 +724,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -700,8 +735,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -709,8 +744,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); } } } @@ -726,10 +761,9 @@ public void TestBitPacking15BitsUnsigned() writer.VerifyCanWrite(2); ushort value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); for (var i = 0; i < 15; ++i) { @@ -738,8 +772,8 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -748,8 +782,8 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); } } @@ -766,10 +800,9 @@ public void TestBitPacking14BitsSigned() writer.VerifyCanWrite(2); short value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); for (var i = 0; i < 14; ++i) { @@ -779,8 +812,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -788,8 +821,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -799,8 +832,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -808,8 +841,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); } } } @@ -1163,12 +1196,15 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteValuePacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out Ray outVal); - Assert.AreEqual(v.origin, outVal.origin); - Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); - Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); - Assert.AreEqual(v.direction.z, outVal.direction.z, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + Assert.AreEqual(v.direction.z, outVal.direction.z, 0.00001); + } } } } @@ -1180,12 +1216,15 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteObjectPacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); - Assert.AreEqual(v.origin, ((Ray)outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); - Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); + Assert.AreEqual(v.origin, ((Ray) outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray) outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray) outVal).direction.y, 0.00001); + Assert.AreEqual(v.direction.z, ((Ray) outVal).direction.z, 0.00001); + } } } } @@ -1205,11 +1244,14 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteValuePacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out Ray2D outVal); - Assert.AreEqual(v.origin, outVal.origin); - Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); - Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray2D outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + } } } } @@ -1221,11 +1263,14 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteObjectPacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); - Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); + Assert.AreEqual(v.origin, ((Ray2D) outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray2D) outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray2D) outVal).direction.y, 0.00001); + } } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 49e423d04f..9cb416300c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -117,13 +117,11 @@ private void VerifyPositionAndLength(ref FastBufferReader reader, int length, st private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged { - NativeArray underlyingArray = writer.GetNativeArray(); - WriteCheckBytes(ref writer, writeSize, failMessage); - FastBufferReader reader = new FastBufferReader(underlyingArray); + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); - VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + VerifyPositionAndLength(ref reader, writer.Length, failMessage); VerifyCheckBytes(ref reader, writeSize, failMessage); @@ -150,9 +148,12 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); - reader.ReadValue(out T result); - Assert.AreEqual(valueToTest, result); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); + reader.ReadValue(out T result); + Assert.AreEqual(valueToTest, result); + } } } private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged @@ -170,9 +171,12 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - - reader.ReadValueSafe(out T result); - Assert.AreEqual(valueToTest, result); + + using (reader) + { + reader.ReadValueSafe(out T result); + Assert.AreEqual(valueToTest, result); + } } } @@ -189,9 +193,12 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged writer.WriteObject(valueToTest); var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - - reader.ReadObject(out object result, typeof(T)); - Assert.AreEqual(valueToTest, result); + + using (reader) + { + reader.ReadObject(out object result, typeof(T)); + Assert.AreEqual(valueToTest, result); + } } } @@ -211,8 +218,6 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -220,14 +225,17 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged WriteCheckBytes(ref writer, writeSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); - Assert.IsTrue(reader.VerifyCanRead(writeSize)); - reader.ReadValue(out T[] result); - VerifyArrayEquality(valueToTest, result, 0); + Assert.IsTrue(reader.VerifyCanRead(writeSize)); + reader.ReadValue(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); - VerifyCheckBytes(ref reader, writeSize); + VerifyCheckBytes(ref reader, writeSize); + } } } @@ -237,21 +245,22 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteValueSafe(valueToTest); WriteCheckBytes(ref writer, writeSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadValueSafe(out T[] result); - VerifyArrayEquality(valueToTest, result, 0); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadValueSafe(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); - VerifyCheckBytes(ref reader, writeSize); + VerifyCheckBytes(ref reader, writeSize); + } } } @@ -262,21 +271,22 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); WriteCheckBytes(ref writer, writeSize + 1); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadObject(out object result, typeof(T[])); - VerifyArrayEquality(valueToTest, (T[])result, 0); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadObject(out object result, typeof(T[])); + VerifyArrayEquality(valueToTest, (T[]) result, 0); - VerifyCheckBytes(ref reader, writeSize + 1); + VerifyCheckBytes(ref reader, writeSize + 1); + } } } #endregion @@ -1547,21 +1557,22 @@ public void TestReadingString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1575,19 +1586,20 @@ public void TestReadingStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - writer.WriteValueSafe(valueToTest); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadValueSafe(out string result); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadValueSafe(out string result); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1601,19 +1613,20 @@ public void TestReadingStringAsObject() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - writer.WriteObject(valueToTest); WriteCheckBytes(ref writer, serializedValueSize+1); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadObject(out object result, typeof(string)); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadObject(out object result, typeof(string)); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize+1); + VerifyCheckBytes(ref reader, serializedValueSize + 1); + } } } @@ -1626,21 +1639,22 @@ public void TestReadingOneByteString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest, true); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result, true); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result, true); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1653,19 +1667,20 @@ public void TestReadingOneByteStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - writer.WriteValueSafe(valueToTest, true); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadValueSafe(out string result, true); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadValueSafe(out string result, true); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1683,24 +1698,27 @@ public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulo var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; WriteCheckBytes(ref writer, count, failMessage); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - mask = (mask << 8) | 0b11111111; + VerifyPositionAndLength(ref reader, writer.Length, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + reader.ReadPartialValue(out ulong result, count); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); } - - reader.ReadPartialValue(out ulong result, count); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); } } [Test] - public unsafe void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) { var random = new Random(); var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); @@ -1708,55 +1726,34 @@ public unsafe void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); writer.WritePartialValue(valueToTest, count, 2); var failMessage = $"TestReadingPartialValuesWithOffsets failed with value {valueToTest}"; WriteCheckBytes(ref writer, count, failMessage); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - mask = (mask << 8) | 0b11111111; - } + VerifyPositionAndLength(ref reader, writer.Length, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - mask <<= 16; - - reader.ReadPartialValue(out ulong result, count, 2); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); - } - } - - [Test] - public void TestToArray() - { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } - using (writer) - { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); + mask <<= 16; - var reader = new FastBufferReader(writer.GetNativeArray()); - var array = reader.ToArray(); - var underlyingArray = writer.GetNativeArray(); - for(var i = 0; i < array.Length; ++i) - { - Assert.AreEqual(array[i], underlyingArray[i]); + reader.ReadPartialValue(out ulong result, count, 2); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); } } } [Test] - public unsafe void TestGetUnsafePtr() + public unsafe void TestToArray() { var testStruct = GetTestStruct(); var requiredSize = FastBufferWriter.GetWriteSize(testStruct); @@ -1766,13 +1763,17 @@ public unsafe void TestGetUnsafePtr() { writer.VerifyCanWrite(requiredSize); writer.WriteValue(testStruct); - var reader = new FastBufferReader(writer.GetNativeArray()); - var ptr = reader.GetUnsafePtr(); - var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); - Assert.IsTrue(underlyingArrayPtr == ptr); - var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); - Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + var array = reader.ToArray(); + var underlyingArray = writer.GetUnsafePtr(); + for (var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } + } } } @@ -1780,9 +1781,11 @@ public unsafe void TestGetUnsafePtr() public void TestThrowingIfBoundsCheckingSkipped() { var writer = new FastBufferWriter(100, Allocator.Temp); - var emptyReader = new FastBufferReader(new NativeArray(100, Allocator.Temp), 0, 0); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp, 0); + nativeArray.Dispose(); - using (emptyReader.GetNativeArray()) + using (emptyReader) using (writer) { Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); @@ -1797,14 +1800,17 @@ public void TestThrowingIfBoundsCheckingSkipped() writer.WriteByte(1); writer.WriteByte(2); writer.WriteByte(3); - var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); - Assert.Throws(() => { reader.ReadValue(out int i); }); - Assert.Throws(() => { reader.ReadValue(out byte b); }); - Assert.IsTrue(reader.VerifyCanRead(3)); - reader.ReadByte(out byte b); - reader.ReadByte(out b); - reader.ReadByte(out b); - Assert.Throws(() => { reader.ReadValue(out byte b); }); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.Throws(() => { reader.ReadValue(out int i); }); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + Assert.IsTrue(reader.VerifyCanRead(3)); + reader.ReadByte(out byte b); + reader.ReadByte(out b); + reader.ReadByte(out b); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + } } } @@ -1830,21 +1836,27 @@ public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() writer.WriteValueSafe(bytes); writer.WriteValueSafe(""); - var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); - Assert.IsTrue(reader.VerifyCanRead(writer.Length)); - using (var context = reader.EnterBitwiseContext()) - { - Assert.Throws(() => { reader.ReadByte(out byte b); }); - Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); - Assert.Throws(() => { reader.ReadValue(out i); }); - Assert.Throws(() => { reader.ReadValue(out bytes); }); - Assert.Throws(() => { reader.ReadValue(out string s); }); - - Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); - Assert.Throws(() => { reader.ReadBytesSafe(ref bytes, bytes.Length); }); - Assert.Throws(() => { reader.ReadValueSafe(out i); }); - Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); - Assert.Throws(() => { reader.ReadValueSafe(out string s); }); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(writer.Length)); + using (var context = reader.EnterBitwiseContext()) + { + Assert.Throws(() => { reader.ReadByte(out byte b); }); + Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); + Assert.Throws(() => { reader.ReadValue(out i); }); + Assert.Throws(() => { reader.ReadValue(out bytes); }); + Assert.Throws(() => { reader.ReadValue(out string s); }); + + Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); + Assert.Throws(() => + { + reader.ReadBytesSafe(ref bytes, bytes.Length); + }); + Assert.Throws(() => { reader.ReadValueSafe(out i); }); + Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); + Assert.Throws(() => { reader.ReadValueSafe(out string s); }); + } } } } @@ -1852,8 +1864,10 @@ public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() [Test] public void TestVerifyCanReadIsRelativeToPositionAndNotAllowedReadPosition() { - var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); - using (reader.GetNativeArray()) + var nativeArray = new NativeArray(100, Allocator.Temp); + var reader = new FastBufferReader(nativeArray, Allocator.Temp, 100); + nativeArray.Dispose(); + using (reader) { reader.VerifyCanRead(100); reader.ReadByte(out byte b); @@ -1876,41 +1890,44 @@ public void TestSeeking() writer.WriteByteSafe(4); writer.WriteByteSafe(0); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.Seek(5); - reader.ReadByteSafe(out byte b); - Assert.AreEqual(reader.Position, 6); - Assert.AreEqual(reader.Length, 100); - Assert.AreEqual(0, b); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.Seek(5); + reader.ReadByteSafe(out byte b); + Assert.AreEqual(reader.Position, 6); + Assert.AreEqual(reader.Length, writer.Length); + Assert.AreEqual(0, b); - reader.Seek(0); - reader.ReadByteSafe(out b); - Assert.AreEqual(reader.Position, 1); - Assert.AreEqual(reader.Length, 100); - Assert.AreEqual(1, b); + reader.Seek(0); + reader.ReadByteSafe(out b); + Assert.AreEqual(reader.Position, 1); + Assert.AreEqual(reader.Length, writer.Length); + Assert.AreEqual(1, b); - reader.Seek(10); - Assert.AreEqual(reader.Position, 10); - Assert.AreEqual(reader.Length, 100); + reader.Seek(10); + Assert.AreEqual(reader.Position, writer.Length); + Assert.AreEqual(reader.Length, writer.Length); - reader.Seek(2); - reader.ReadByteSafe(out b); - Assert.AreEqual(2, b); + reader.Seek(2); + reader.ReadByteSafe(out b); + Assert.AreEqual(2, b); - reader.Seek(1); - reader.ReadByteSafe(out b); - Assert.AreEqual(3, b); + reader.Seek(1); + reader.ReadByteSafe(out b); + Assert.AreEqual(3, b); - reader.Seek(4); - reader.ReadByteSafe(out b); - Assert.AreEqual(4, b); + reader.Seek(4); + reader.ReadByteSafe(out b); + Assert.AreEqual(4, b); - reader.Seek(3); - reader.ReadByteSafe(out b); - Assert.AreEqual(5, b); + reader.Seek(3); + reader.ReadByteSafe(out b); + Assert.AreEqual(5, b); - Assert.AreEqual(reader.Position, 4); - Assert.AreEqual(reader.Length, 100); + Assert.AreEqual(reader.Position, 4); + Assert.AreEqual(reader.Length, writer.Length); + } } } @@ -1935,7 +1952,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) NetworkTransport = obj.AddComponent() }; - networkManager.StartHost(); + networkManager.StartServer(); try { @@ -1944,7 +1961,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) finally { GameObject.DestroyImmediate(obj); - networkManager.StopHost(); + networkManager.StopServer(); } } @@ -1962,11 +1979,14 @@ public void TestNetworkBehaviour() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); - reader.ReadValue(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); + reader.ReadValue(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + } + } }); } @@ -1985,10 +2005,13 @@ public void TestNetworkObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); - reader.ReadValue(out NetworkObject result); - Assert.AreSame(result, networkObject); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); + reader.ReadValue(out NetworkObject result); + Assert.AreSame(result, networkObject); + } } }); } @@ -2007,10 +2030,13 @@ public void TestGameObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); - reader.ReadValue(out GameObject result); - Assert.AreSame(result, obj); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); + reader.ReadValue(out GameObject result); + Assert.AreSame(result, obj); + } } }); } @@ -2027,9 +2053,12 @@ public void TestNetworkBehaviourSafe() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadValueSafe(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + } } }); } @@ -2046,9 +2075,12 @@ public void TestNetworkObjectSafe() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadValueSafe(out NetworkObject result); - Assert.AreSame(result, networkObject); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out NetworkObject result); + Assert.AreSame(result, networkObject); + } } }); } @@ -2065,9 +2097,12 @@ public void TestGameObjectSafe() Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadValueSafe(out GameObject result); - Assert.AreSame(result, obj); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out GameObject result); + Assert.AreSame(result, obj); + } } }); } @@ -2085,9 +2120,12 @@ public void TestNetworkBehaviourAsObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadObject(out object result, typeof(NetworkBehaviour)); - Assert.AreSame(result, networkBehaviour); + var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); + using (reader) + { + reader.ReadObject(out object result, typeof(NetworkBehaviour)); + Assert.AreSame(result, networkBehaviour); + } } }); } @@ -2105,9 +2143,12 @@ public void TestNetworkObjectAsObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadObject(out object result, typeof(NetworkObject)); - Assert.AreSame(result, networkObject); + var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); + using (reader) + { + reader.ReadObject(out object result, typeof(NetworkObject)); + Assert.AreSame(result, networkObject); + } } }); } @@ -2125,9 +2166,12 @@ public void TestGameObjectAsObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadObject(out object result, typeof(GameObject)); - Assert.AreSame(result, obj); + var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); + using (reader) + { + reader.ReadObject(out object result, typeof(GameObject)); + Assert.AreSame(result, obj); + } } }); } @@ -2135,12 +2179,12 @@ public void TestGameObjectAsObject() [Test] public void TestVerifyInternalDoesntReduceAllowedWritePoint() { - var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); - using (reader.GetNativeArray()) + var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp), Allocator.Temp); + using (reader) { reader.VerifyCanRead(25); reader.VerifyCanReadInternal(5); - Assert.AreEqual(reader.m_InternalData.AllowedReadMark, 25); + Assert.AreEqual(reader.m_AllowedReadMark, 25); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 2291627605..5401a4e5f5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -98,13 +98,13 @@ private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string Assert.AreEqual(writeSize+2, writer.Length, failMessage); } - private void VerifyCheckBytes(NativeArray underlyingArray, int writeSize, string failMessage = "") + private void VerifyCheckBytes(byte[] underlyingArray, int writeSize, string failMessage = "") { Assert.AreEqual(0x80, underlyingArray[writeSize], failMessage); Assert.AreEqual(0xFF, underlyingArray[writeSize+1], failMessage); } - private unsafe void VerifyBytewiseEquality(T value, NativeArray underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged + private unsafe void VerifyBytewiseEquality(T value, byte[] underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged { byte* asBytePointer = (byte*) &value; for (var i = 0; i < size; ++i) @@ -113,9 +113,9 @@ private unsafe void VerifyBytewiseEquality(T value, NativeArray underly } } - private unsafe void VerifyTypedEquality(T value, NativeArray underlyingArray) where T: unmanaged + private unsafe void VerifyTypedEquality(T value, byte* unsafePtr) where T: unmanaged { - T* checkValue = (T*) underlyingArray.GetUnsafePtr(); + T* checkValue = (T*) unsafePtr; Assert.AreEqual(value, *checkValue); } @@ -125,19 +125,20 @@ private void VerifyPositionAndLength(ref FastBufferWriter writer, int position, Assert.AreEqual(position, writer.Length, failMessage); } - private void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged { - NativeArray underlyingArray = writer.GetNativeArray(); VerifyPositionAndLength(ref writer, writeSize, failMessage); WriteCheckBytes(ref writer, writeSize, failMessage); + var underlyingArray = writer.ToArray(); + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, writeSize, failMessage); VerifyCheckBytes(underlyingArray, writeSize, failMessage); - VerifyTypedEquality(valueToTest, underlyingArray); + VerifyTypedEquality(valueToTest, writer.GetUnsafePtr()); } #endregion @@ -196,14 +197,14 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private unsafe void VerifyArrayEquality(T[] value, NativeArray underlyingArray, int offset) where T: unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged { - int* sizeValue = (int*)((byte*)underlyingArray.GetUnsafePtr() + offset); + int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); fixed (T* asTPointer = value) { - T* underlyingTArray = (T*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + offset); + T* underlyingTArray = (T*) (unsafePtr + sizeof(int) + offset); for (var i = 0; i < value.Length; ++i) { Assert.AreEqual(asTPointer[i], underlyingTArray[i]); @@ -217,7 +218,6 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -227,8 +227,9 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged WriteCheckBytes(ref writer, writeSize); - VerifyArrayEquality(valueToTest, underlyingArray, 0); + VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), 0); + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, writeSize); } } @@ -239,7 +240,6 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); @@ -248,8 +248,9 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged WriteCheckBytes(ref writer, writeSize); - VerifyArrayEquality(valueToTest, underlyingArray, 0); + VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), 0); + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, writeSize); } } @@ -261,18 +262,18 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); - Assert.AreEqual(0, writer.GetNativeArray()[0]); + Assert.AreEqual(0, writer.ToArray()[0]); VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); WriteCheckBytes(ref writer, writeSize + sizeof(byte)); - VerifyArrayEquality(valueToTest, underlyingArray, sizeof(byte)); + VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), sizeof(byte)); + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, writeSize + sizeof(byte)); } } @@ -1532,7 +1533,6 @@ public unsafe void TestWritingString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); @@ -1540,18 +1540,19 @@ public unsafe void TestWritingString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1566,25 +1567,25 @@ public unsafe void TestWritingStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); writer.WriteValueSafe(valueToTest); VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*) ((byte*) writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1599,25 +1600,25 @@ public unsafe void TestWritingStringAsObject() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); writer.WriteObject(valueToTest); VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); - int* sizeValue = (int*) ((byte*)underlyingArray.GetUnsafePtr() + sizeof(byte)); + int* sizeValue = (int*) (writer.GetUnsafePtr() + sizeof(byte)); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + sizeof(byte)); + char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize + sizeof(byte)); } } @@ -1631,7 +1632,6 @@ public unsafe void TestWritingOneByteString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest, true); @@ -1639,18 +1639,19 @@ public unsafe void TestWritingOneByteString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1664,25 +1665,25 @@ public unsafe void TestWritingOneByteStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); writer.WriteValueSafe(valueToTest, true); VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1695,7 +1696,6 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); writer.WritePartialValue(valueToTest, count); @@ -1703,6 +1703,7 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; VerifyPositionAndLength(ref writer, count, failMessage); WriteCheckBytes(ref writer, count, failMessage); + var underlyingArray = writer.ToArray(); VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, count, failMessage); VerifyCheckBytes(underlyingArray, count, failMessage); @@ -1712,7 +1713,7 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + ulong* checkValue = (ulong*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); } } @@ -1726,7 +1727,6 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); writer.WritePartialValue(valueToTest, count, 2); @@ -1734,6 +1734,7 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, VerifyPositionAndLength(ref writer, count, failMessage); WriteCheckBytes(ref writer, count, failMessage); + var underlyingArray = writer.ToArray(); VerifyBytewiseEquality(valueToTest, underlyingArray, 2, 0, count, failMessage); VerifyCheckBytes(underlyingArray, count, failMessage); @@ -1743,7 +1744,7 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + ulong* checkValue = (ulong*) writer.GetUnsafePtr(); Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); } } @@ -1760,7 +1761,7 @@ public void TestToArray() writer.VerifyCanWrite(requiredSize); writer.WriteValue(testStruct); var array = writer.ToArray(); - var underlyingArray = writer.GetNativeArray(); + var underlyingArray = writer.ToArray(); for(var i = 0; i < array.Length; ++i) { Assert.AreEqual(array[i], underlyingArray[i]); @@ -1768,26 +1769,6 @@ public void TestToArray() } } - [Test] - public unsafe void TestGetUnsafePtr() - { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); - - using (writer) - { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); - var ptr = writer.GetUnsafePtr(); - var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); - Assert.IsTrue(underlyingArrayPtr == ptr); - - var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); - Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); - } - } - [Test] public void TestThrowingIfBoundsCheckingSkipped() { @@ -1900,7 +1881,7 @@ public void TestSeeking() Assert.AreEqual(writer.Length, 10); var expected = new byte[] {1, 3, 2, 5, 4, 0}; - var underlyingArray = writer.GetNativeArray(); + var underlyingArray = writer.ToArray(); for (var i = 0; i < expected.Length; ++i) { Assert.AreEqual(expected[i], underlyingArray[i]); @@ -1958,6 +1939,7 @@ public void TestGrowth() // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + var preGrowthLength = writer.Position; // First writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); @@ -1968,17 +1950,17 @@ public void TestGrowth() // First writer shouldn't have grown Assert.AreEqual(150, writer.Capacity); - Assert.AreEqual(150, writer.GetNativeArray().Length); + Assert.AreEqual(preGrowthLength, writer.ToArray().Length); // First growth doubles the size Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); // Write right up to the very end of the buffer, verify it doesn't grow growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); // Go to the end of the buffer and grow again @@ -1988,14 +1970,14 @@ public void TestGrowth() // Second growth caps it at maxSize Assert.AreEqual(500, growingWriter.Capacity); - Assert.AreEqual(500, growingWriter.GetNativeArray().Length); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - VerifyBytewiseEquality(testStruct, writer.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); // Verify the growth properly copied the existing data - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); } } @@ -2045,8 +2027,8 @@ public void TestNetworkBehaviour() writer.WriteValue(networkBehaviour); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong), sizeof(ushort)); } }); @@ -2065,7 +2047,7 @@ public void TestNetworkObject() writer.WriteValue(networkObject); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2083,7 +2065,7 @@ public void TestGameObject() writer.WriteValue(obj); Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2099,8 +2081,8 @@ public void TestNetworkBehaviourSafe() writer.WriteValueSafe(networkBehaviour); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong), sizeof(ushort)); } }); @@ -2117,7 +2099,7 @@ public void TestNetworkObjectSafe() writer.WriteValueSafe(networkObject); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2133,7 +2115,7 @@ public void TestGameObjectSafe() writer.WriteValueSafe(obj); Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2150,9 +2132,9 @@ public void TestNetworkBehaviourAsObject() writer.WriteObject(networkBehaviour); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - Assert.AreEqual(0, writer.GetNativeArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + Assert.AreEqual(0, writer.ToArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong)+1, sizeof(ushort)); } }); @@ -2170,8 +2152,8 @@ public void TestNetworkObjectAsObject() writer.WriteObject(networkObject); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - Assert.AreEqual(0, writer.GetNativeArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(0, writer.ToArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); } }); } @@ -2188,8 +2170,8 @@ public void TestGameObjectAsObject() writer.WriteObject(obj); Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - Assert.AreEqual(0, writer.GetNativeArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(0, writer.ToArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); } }); } @@ -2202,7 +2184,7 @@ public void TestVerifyInternalDoesntReduceAllowedWritePoint() { writer.VerifyCanWrite(25); writer.VerifyCanWriteInternal(5); - Assert.AreEqual(writer.m_InternalData.AllowedWriteMark, 25); + Assert.AreEqual(writer.m_AllowedWriteMark, 25); } } #endregion From fc944c48f3907919a75ccf95c70fbc0e6e01a1b9 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 19 Aug 2021 17:40:17 -0500 Subject: [PATCH 06/58] Added utility ref struct "Ref" to more generically support wrapping values in other ref structs in a capture-by-reference style, updated BitReader and BitWriter to use that. Also aggressively inlining properties in FBW and FBR. --- .../Runtime/Serialization/BitReader.cs | 27 ++++++++--------- .../Runtime/Serialization/BitWriter.cs | 30 ++++++++----------- .../Runtime/Serialization/FastBufferReader.cs | 15 ++++++++-- .../Runtime/Serialization/FastBufferWriter.cs | 21 +++++++++++-- .../Runtime/Utility.meta | 3 ++ .../Runtime/Utility/Ref.cs | 23 ++++++++++++++ .../Runtime/Utility/Ref.cs.meta | 11 +++++++ 7 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index a194e228ad..e4ed71af02 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -8,7 +8,7 @@ namespace Unity.Multiplayer.Netcode { public ref struct BitReader { - private unsafe FastBufferReader* m_Reader; + private Ref m_Reader; private unsafe byte* m_BufferPointer; private int m_Position; private const int BITS_PER_BYTE = 8; @@ -20,37 +20,34 @@ public ref struct BitReader /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() + public bool BitAligned { - return (m_BitPosition & 7) == 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (m_BitPosition & 7) == 0; } internal unsafe BitReader(ref FastBufferReader reader) { - fixed (FastBufferReader* readerPtr = &reader) - { - m_Reader = readerPtr; - } + m_Reader = new Ref(ref reader); - m_BufferPointer = m_Reader->m_BufferPointer + m_Reader->Position; - m_Position = m_Reader->Position; + m_BufferPointer = m_Reader.Value.m_BufferPointer + m_Reader.Value.Position; + m_Position = m_Reader.Value.Position; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseReadMark = (m_Reader->m_AllowedReadMark - m_Position) * BITS_PER_BYTE; + m_AllowedBitwiseReadMark = (m_Reader.Value.m_AllowedReadMark - m_Position) * BITS_PER_BYTE; #endif } public unsafe void Dispose() { var bytesWritten = m_BitPosition >> 3; - if (!BitAligned()) + if (!BitAligned) { // Accounting for the partial read ++bytesWritten; } - m_Reader->CommitBitwiseReads(bytesWritten); + m_Reader.Value.CommitBitwiseReads(bytesWritten); } public unsafe bool VerifyCanReadBits(int bitCount) @@ -63,7 +60,7 @@ public unsafe bool VerifyCanReadBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Reader->m_Position + totalBytesWrittenInBitwiseContext > m_Reader->m_Length) + if (m_Reader.Value.m_Position + totalBytesWrittenInBitwiseContext > m_Reader.Value.m_Length) { return false; } @@ -101,7 +98,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &val; - if (BitAligned()) + if (BitAligned) { if (wholeBytes != 0) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index b941d36f39..03658b0b2d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -6,7 +6,7 @@ namespace Unity.Multiplayer.Netcode { public ref struct BitWriter { - private unsafe FastBufferWriter* m_Writer; + private Ref m_Writer; private unsafe byte* m_BufferPointer; private int m_Position; internal int m_BitPosition; @@ -18,25 +18,21 @@ public ref struct BitWriter /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() + public bool BitAligned { - return (m_BitPosition & 7) == 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (m_BitPosition & 7) == 0; } internal unsafe BitWriter(ref FastBufferWriter writer) { - fixed (FastBufferWriter* internalDataPtr = &writer) - { - m_Writer = internalDataPtr; - } - + m_Writer = new Ref(ref writer); m_BufferPointer = writer.m_BufferPointer + writer.m_Position; m_Position = writer.m_Position; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseWriteMark = (m_Writer->m_AllowedWriteMark - m_Writer->Position) * BITS_PER_BYTE; + m_AllowedBitwiseWriteMark = (m_Writer.Value.m_AllowedWriteMark - m_Writer.Value.Position) * BITS_PER_BYTE; #endif } @@ -50,12 +46,12 @@ public unsafe bool VerifyCanWriteBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer->m_Capacity) + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.m_Capacity) { - if (m_Writer->m_Capacity < m_Writer->m_MaxCapacity) + if (m_Writer.Value.m_Capacity < m_Writer.Value.m_MaxCapacity) { - m_Writer->Grow(); - m_BufferPointer = m_Writer->m_BufferPointer; + m_Writer.Value.Grow(); + m_BufferPointer = m_Writer.Value.m_BufferPointer + m_Writer.Value.m_Position; } else { @@ -71,13 +67,13 @@ public unsafe bool VerifyCanWriteBits(int bitCount) public unsafe void Dispose() { var bytesWritten = m_BitPosition >> 3; - if (!BitAligned()) + if (!BitAligned) { // Accounting for the partial write ++bytesWritten; } - m_Writer->CommitBitwiseWrites(bytesWritten); + m_Writer.Value.CommitBitwiseWrites(bytesWritten); } /// @@ -107,7 +103,7 @@ public unsafe void WriteBits(ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &value; - if (BitAligned()) + if (BitAligned) { if (wholeBytes != 0) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index a46f6967a1..053f5ce4e0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -138,9 +138,18 @@ public BitReader EnterBitwiseContext() #endif return new BitReader(ref this); } - - public int Position => m_Position; - public int Length => m_Length; + + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Length; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 6d3df83614..73152a5045 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -100,9 +100,24 @@ public BitWriter EnterBitwiseContext() return new BitWriter(ref this); } - public int Position => m_Position; - public int Capacity => m_Capacity; - public int Length => m_Position > m_Length ? m_Position : m_Length; + + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Capacity; + } + + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position > m_Length ? m_Position : m_Length; + } internal unsafe void Grow() { diff --git a/com.unity.netcode.gameobjects/Runtime/Utility.meta b/com.unity.netcode.gameobjects/Runtime/Utility.meta new file mode 100644 index 0000000000..a71098491b --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 07815748c1ee48a69d6981ec990575ed +timeCreated: 1629412677 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs new file mode 100644 index 0000000000..f58938a7e7 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct Ref where T: unmanaged + { + private unsafe T* m_Value; + + public unsafe Ref(ref T value) + { + fixed (T* ptr = &value) + { + m_Value = ptr; + } + } + + public unsafe ref T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref *m_Value; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta new file mode 100644 index 0000000000..56afe0844f --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00adc41a9c8699349a50dba23dbd46a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From cb95862194f8cca0e5355439f7c1a5b5ce46056a Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 23 Aug 2021 17:29:12 -0500 Subject: [PATCH 07/58] BufferSerializer and tests. --- .../Runtime/Serialization/BitReader.cs | 2 +- .../Runtime/Serialization/BitWriter.cs | 4 +- .../Runtime/Serialization/BufferSerializer.cs | 105 +++ .../Serialization/BufferSerializer.cs.meta | 11 + .../Serialization/BufferSerializerReader.cs | 119 ++++ .../BufferSerializerReader.cs.meta | 11 + .../Serialization/BufferSerializerWriter.cs | 119 ++++ .../BufferSerializerWriter.cs.meta | 11 + .../Runtime/Serialization/FastBufferReader.cs | 6 +- .../Runtime/Serialization/FastBufferWriter.cs | 2 +- .../Serialization/IBufferSerializer.cs | 42 ++ .../Serialization/IBufferSerializer.cs.meta | 11 + .../Runtime/Utility/Ref.cs | 4 +- .../Runtime/Utility/RefArray.cs | 92 +++ .../Runtime/Utility/RefArray.cs.meta | 11 + .../Editor/Serialization/BitReaderTests.cs | 27 + .../Editor/Serialization/BitWriterTests.cs | 26 + .../Serialization/BufferSerializerTests.cs | 662 ++++++++++++++++++ .../BufferSerializerTests.cs.meta | 11 + 19 files changed, 1268 insertions(+), 8 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index e4ed71af02..5cda1a6bf6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -188,7 +188,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); break; case 7: - *(uint*) ptr = *(uint*)ptr; + *(uint*) ptr = *(uint*)bufferPointer; *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); *(ptr+6) = *(bufferPointer+6); break; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 03658b0b2d..1af6025a90 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -195,11 +195,11 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy *(bufferPointer+4) = *(ptr+4); break; case 6: - *(uint*) bufferPointer = *(uint*) &ptr; + *(uint*) bufferPointer = *(uint*)ptr; *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); break; case 7: - *(uint*) bufferPointer = *(uint*) &value; + *(uint*) bufferPointer = *(uint*)ptr; *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); *(bufferPointer+6) = *(ptr+6); break; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs new file mode 100644 index 0000000000..5e409704e9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -0,0 +1,105 @@ +using System; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public ref struct BufferSerializer where T : IBufferSerializerImplementation + { + private T m_Implementation; + + public bool IsReader => m_Implementation.IsReader; + public bool IsWriter => m_Implementation.IsWriter; + + public BufferSerializer(T implementation) + { + m_Implementation = implementation; + } + + public ref FastBufferReader GetFastBufferReader() + { + return ref m_Implementation.GetFastBufferReader(); + } + public ref FastBufferWriter GetFastBufferWriter() + { + return ref m_Implementation.GetFastBufferWriter(); + } + + public void SerializeValue(ref object value, Type type, bool isNullable = false) + { + m_Implementation.SerializeValue(ref value, type, isNullable); + } + public void SerializeValue(ref INetworkSerializable value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref GameObject value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref NetworkObject value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref NetworkBehaviour value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref string s, bool oneByteChars = false) + { + m_Implementation.SerializeValue(ref s, oneByteChars); + } + public void SerializeValue(ref T[] array) where T : unmanaged + { + m_Implementation.SerializeValue(ref array); + } + public void SerializeValue(ref byte value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref T value) where T : unmanaged + { + m_Implementation.SerializeValue(ref value); + } + + // Has to have a different name to avoid conflicting with "where T: unmananged" + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Implementation.SerializeNetworkSerializable(ref value); + } + + public bool PreCheck(int amount) + { + return m_Implementation.PreCheck(amount); + } + + public void SerializeValuePreChecked(ref GameObject value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref NetworkObject value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref NetworkBehaviour value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) + { + m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); + } + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged + { + m_Implementation.SerializeValuePreChecked(ref array); + } + public void SerializeValuePreChecked(ref byte value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref T value) where T : unmanaged + { + m_Implementation.SerializeValuePreChecked(ref value); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta new file mode 100644 index 0000000000..fe70dbe5be --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fca519b9bc3b32e4f9bd7cd138d690af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs new file mode 100644 index 0000000000..7016806da9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -0,0 +1,119 @@ +using System; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public struct BufferSerializerReader : IBufferSerializerImplementation + { + private Ref m_Reader; + + public BufferSerializerReader(ref FastBufferReader reader) + { + m_Reader = new Ref(ref reader); + } + + public bool IsReader => true; + public bool IsWriter => false; + + public ref FastBufferReader GetFastBufferReader() + { + return ref m_Reader.Value; + } + + public ref FastBufferWriter GetFastBufferWriter() + { + throw new InvalidOperationException("Cannot retrieve a FastBufferWriter from a serializer where IsWriter = false"); + } + + public void SerializeValue(ref object value, Type type, bool isNullable = false) + { + m_Reader.Value.ReadObject(out value, type, isNullable); + } + + public void SerializeValue(ref INetworkSerializable value) + { + m_Reader.Value.ReadNetworkSerializable(out value); + } + + public void SerializeValue(ref GameObject value) + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeValue(ref NetworkObject value) + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeValue(ref NetworkBehaviour value) + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeValue(ref string s, bool oneByteChars = false) + { + m_Reader.Value.ReadValueSafe(out s, oneByteChars); + } + + public void SerializeValue(ref T[] array) where T : unmanaged + { + m_Reader.Value.ReadValueSafe(out array); + } + + public void SerializeValue(ref byte value) + { + m_Reader.Value.ReadByteSafe(out value); + } + + public void SerializeValue(ref T value) where T : unmanaged + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Reader.Value.ReadNetworkSerializable(out value); + } + + public bool PreCheck(int amount) + { + return m_Reader.Value.VerifyCanRead(amount); + } + + public void SerializeValuePreChecked(ref GameObject value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref NetworkObject value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref NetworkBehaviour value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) + { + m_Reader.Value.ReadValue(out s, oneByteChars); + } + + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged + { + m_Reader.Value.ReadValue(out array); + } + + public void SerializeValuePreChecked(ref byte value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref T value) where T : unmanaged + { + m_Reader.Value.ReadValue(out value); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta new file mode 100644 index 0000000000..ed94cf148a --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70dd17b6c14f7cd43ba5380d01cf91ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs new file mode 100644 index 0000000000..2b16b718fa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -0,0 +1,119 @@ +using System; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public struct BufferSerializerWriter : IBufferSerializerImplementation + { + private Ref m_Writer; + + public BufferSerializerWriter(ref FastBufferWriter writer) + { + m_Writer = new Ref(ref writer); + } + + public bool IsReader => false; + public bool IsWriter => true; + + public ref FastBufferReader GetFastBufferReader() + { + throw new InvalidOperationException("Cannot retrieve a FastBufferReader from a serializer where IsReader = false"); + } + + public ref FastBufferWriter GetFastBufferWriter() + { + return ref m_Writer.Value; + } + + public void SerializeValue(ref object value, Type type, bool isNullable = false) + { + m_Writer.Value.WriteObject(value, isNullable); + } + + public void SerializeValue(ref INetworkSerializable value) + { + m_Writer.Value.WriteNetworkSerializable(value); + } + + public void SerializeValue(ref GameObject value) + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeValue(ref NetworkObject value) + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeValue(ref NetworkBehaviour value) + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeValue(ref string s, bool oneByteChars = false) + { + m_Writer.Value.WriteValueSafe(s, oneByteChars); + } + + public void SerializeValue(ref T[] array) where T : unmanaged + { + m_Writer.Value.WriteValueSafe(array); + } + + public void SerializeValue(ref byte value) + { + m_Writer.Value.WriteByteSafe(value); + } + + public void SerializeValue(ref T value) where T : unmanaged + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Writer.Value.WriteNetworkSerializable(value); + } + + public bool PreCheck(int amount) + { + return m_Writer.Value.VerifyCanWrite(amount); + } + + public void SerializeValuePreChecked(ref GameObject value) + { + m_Writer.Value.WriteValue(value); + } + + public void SerializeValuePreChecked(ref NetworkObject value) + { + m_Writer.Value.WriteValue(value); + } + + public void SerializeValuePreChecked(ref NetworkBehaviour value) + { + m_Writer.Value.WriteValue(value); + } + + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) + { + m_Writer.Value.WriteValue(s, oneByteChars); + } + + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged + { + m_Writer.Value.WriteValue(array); + } + + public void SerializeValuePreChecked(ref byte value) + { + m_Writer.Value.WriteByte(value); + } + + public void SerializeValuePreChecked(ref T value) where T : unmanaged + { + m_Writer.Value.WriteValue(value); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta new file mode 100644 index 0000000000..183a7bafad --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c05ed8e3061e62147a012cc01a64b5a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 053f5ce4e0..ff9254f9a8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -358,10 +358,10 @@ public void ReadObject(out object value, Type type, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } - /*public void ReadValue(ref T value) where T : INetworkSerializable + public void ReadNetworkSerializable(out T value) where T : INetworkSerializable { - // TODO - }*/ + throw new NotImplementedException(); + } public void ReadValue(out GameObject value) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 73152a5045..508dc1d95e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -340,7 +340,7 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } - public void WriteValue(T value) where T : INetworkSerializable + public void WriteNetworkSerializable(in T value) where T : INetworkSerializable { // TODO } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs new file mode 100644 index 0000000000..f421088482 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public interface IBufferSerializerImplementation + { + public bool IsReader { get; } + public bool IsWriter { get; } + + public ref FastBufferReader GetFastBufferReader(); + public ref FastBufferWriter GetFastBufferWriter(); + + public void SerializeValue(ref object value, Type type, bool isNullable = false); + public void SerializeValue(ref INetworkSerializable value); + public void SerializeValue(ref GameObject value); + public void SerializeValue(ref NetworkObject value); + public void SerializeValue(ref NetworkBehaviour value); + public void SerializeValue(ref string s, bool oneByteChars = false); + public void SerializeValue(ref T[] array) where T : unmanaged; + public void SerializeValue(ref byte value); + public void SerializeValue(ref T value) where T : unmanaged; + + // Has to have a different name to avoid conflicting with "where T: unmananged" + // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables + // So this is provided as an alternative to avoid boxing allocations. + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; + + public bool PreCheck(int amount); + public void SerializeValuePreChecked(ref GameObject value); + public void SerializeValuePreChecked(ref NetworkObject value); + public void SerializeValuePreChecked(ref NetworkBehaviour value); + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false); + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged; + public void SerializeValuePreChecked(ref byte value); + public void SerializeValuePreChecked(ref T value) where T : unmanaged; + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta new file mode 100644 index 0000000000..6dd5e01fcc --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e09b1183d63526341acdacfebdad750d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index f58938a7e7..877ce2ca52 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -2,7 +2,7 @@ namespace Unity.Multiplayer.Netcode { - public ref struct Ref where T: unmanaged + public struct Ref where T: unmanaged { private unsafe T* m_Value; @@ -14,6 +14,8 @@ public unsafe Ref(ref T value) } } + public unsafe bool IsSet => m_Value != null; + public unsafe ref T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs new file mode 100644 index 0000000000..623a87c008 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct RefArray where T: unmanaged + { + public struct RefArrayImplementation : IReadOnlyList + where T : unmanaged + { + internal unsafe T* m_Value; + internal int m_Length; + + internal unsafe RefArrayImplementation(T* ptr, int length) + { + m_Value = ptr; + m_Length = length; + } + + public unsafe ref T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref *m_Value; + } + + public struct Enumerator : IEnumerator, IEnumerator, IDisposable + { + private RefArrayImplementation m_array; + private int m_Index; + + public Enumerator(ref RefArrayImplementation array) + { + this.m_array = array; + this.m_Index = -1; + } + + public void Dispose() + { + } + + public bool MoveNext() + { + ++this.m_Index; + return this.m_Index < this.m_array.Length; + } + + public void Reset() => this.m_Index = -1; + + public T Current => this.m_array[this.m_Index]; + + object IEnumerator.Current => (object) this.Current; + } + + public RefArrayImplementation.Enumerator GetEnumerator() => + new RefArrayImplementation.Enumerator(ref this); + + IEnumerator IEnumerable.GetEnumerator() => + (IEnumerator) new RefArrayImplementation.Enumerator(ref this); + + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator(); + + public int Count => m_Length; + public int Length => m_Length; + + public unsafe T this[int index] + { + get => m_Value[index]; + set => m_Value[index] = value; + } + } + + internal RefArrayImplementation m_Value; + + public unsafe RefArray(T* ptr, int length) + { + m_Value = new RefArrayImplementation(ptr, length); + } + + public unsafe ref RefArrayImplementation Value + { + get + { + fixed (RefArrayImplementation* ptr = &m_Value) + { + return ref *ptr; + } + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta new file mode 100644 index 0000000000..9e30a4c74c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1422b1128ad62024d9e8dac38e009aad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index a395fa43b0..c8fa6a5c4a 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -278,6 +278,33 @@ public void TestReadingMultipleBitsToLongs() } } + [Test] + public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) + { + ulong value = 0xFFFFFFFFFFFFFFFF; + FastBufferReader reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); + using (reader) + { + ulong* asUlong = (ulong*) reader.GetUnsafePtr(); + + Assert.AreEqual(value, *asUlong); + var mask = 0UL; + for (var i = 0; i < numBits; ++i) + { + mask |= (1UL << i); + } + + ulong readValue; + + Assert.IsTrue(reader.VerifyCanRead(sizeof(ulong))); + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out readValue, numBits); + } + Assert.AreEqual(value & mask, readValue); + } + } + [Test] public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index 7614242e93..d794bb6cb9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -233,6 +233,32 @@ public unsafe void TestWritingMultipleBitsFromLongs() } } + [Test] + public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) + { + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); + using (writer) + { + ulong* asUlong = (ulong*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asUlong); + var mask = 0UL; + for (var i = 0; i < numBits; ++i) + { + mask |= (1UL << i); + } + + ulong value = 0xFFFFFFFFFFFFFFFF; + + Assert.IsTrue(writer.VerifyCanWrite(sizeof(ulong))); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(value, numBits); + } + Assert.AreEqual(value & mask, *asUlong); + } + } + [Test] public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs new file mode 100644 index 0000000000..6ea498b5dd --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -0,0 +1,662 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Collections; +using Unity.Multiplayer.Netcode; +using Unity.Netcode.Serialization; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode.EditorTests +{ + public class BufferSerializerTests + { + [Test] + public void TestIsReaderIsWriter() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + Assert.IsFalse(serializer.IsReader); + Assert.IsTrue(serializer.IsWriter); + } + byte[] readBuffer = new byte[4]; + FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + using (reader) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + Assert.IsTrue(serializer.IsReader); + Assert.IsFalse(serializer.IsWriter); + } + } + [Test] + public unsafe void TestGetUnderlyingStructs() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + ref FastBufferWriter underlyingWriter = ref serializer.GetFastBufferWriter(); + fixed (FastBufferWriter* ptr = &underlyingWriter) + { + Assert.IsTrue(ptr == &writer); + } + // Can't use Assert.Throws() because ref structs can't be passed into lambdas. + try + { + serializer.GetFastBufferReader(); + } + catch (InvalidOperationException) + { + // pass + } + + } + byte[] readBuffer = new byte[4]; + FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + using (reader) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + ref FastBufferReader underlyingReader = ref serializer.GetFastBufferReader(); + fixed (FastBufferReader* ptr = &underlyingReader) + { + Assert.IsTrue(ptr == &reader); + } + // Can't use Assert.Throws() because ref structs can't be passed into lambdas. + try + { + serializer.GetFastBufferWriter(); + } + catch (InvalidOperationException) + { + // pass + } + } + } + + // Not reimplementing the entire suite of all value tests for BufferSerializer since they're already tested + // for the underlying structures. These are just basic tests to make sure the correct underlying functions + // are being called. + [Test] + public void TestSerializingObjects() + { + Random random = new Random(); + int value = random.Next(); + object asObj = value; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref asObj, typeof(int)); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + object readValue = 0; + deserializer.SerializeValue(ref readValue, typeof(int)); + + Assert.AreEqual(value, readValue); + } + } + } + + [Test] + public void TestSerializingValues() + { + Random random = new Random(); + int value = random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int readValue = 0; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingBytes() + { + Random random = new Random(); + byte value = (byte)random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + byte readValue = 0; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingArrays() + { + Random random = new Random(); + int[] value = {random.Next(), random.Next(), random.Next()}; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int[] readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingStrings([Values] bool oneBytChars) + { + string value = "I am a test string"; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value, oneBytChars); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + string readValue = null; + deserializer.SerializeValue(ref readValue, oneBytChars); + + Assert.AreEqual(value, readValue); + } + } + } + + + [Test] + public void TestSerializingValuesPreChecked() + { + Random random = new Random(); + int value = random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + serializer.SerializeValuePreChecked(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int readValue = 0; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingBytesPreChecked() + { + Random random = new Random(); + byte value = (byte)random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + serializer.SerializeValuePreChecked(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + byte readValue = 0; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingArraysPreChecked() + { + Random random = new Random(); + int[] value = {random.Next(), random.Next(), random.Next()}; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + serializer.SerializeValuePreChecked(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int[] readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingStringsPreChecked([Values] bool oneBytChars) + { + string value = "I am a test string"; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value, oneBytChars); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); + serializer.SerializeValuePreChecked(ref value, oneBytChars); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + string readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue, oneBytChars); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); + deserializer.SerializeValuePreChecked(ref readValue, oneBytChars); + + Assert.AreEqual(value, readValue); + } + } + } + /* + public void SerializeValue(ref object value, Type type, bool isNullable = false); + public void SerializeValue(ref INetworkSerializable value); + public void SerializeValue(ref GameObject value); + public void SerializeValue(ref NetworkObject value); + public void SerializeValue(ref NetworkBehaviour value); + + public bool PreCheck(int amount); + public void SerializeValuePreChecked(ref GameObject value); + public void SerializeValuePreChecked(ref NetworkObject value); + public void SerializeValuePreChecked(ref NetworkBehaviour value);*/ + + + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + private void RunGameObjectTest(GameObjectTestDelegate testCode) + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkManager.SetSingleton(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartServer(); + + try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopServer(); + } + } + + [Test] + public void TestSerializingGameObjects() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref obj); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + GameObject readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(obj, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkObjects() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref networkObject); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkObject readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(networkObject, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkBehaviours() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref networkBehaviour); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkBehaviour readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(networkBehaviour, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingGameObjectsPreChecked() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref obj); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(obj))); + serializer.SerializeValuePreChecked(ref obj); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + GameObject readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(obj, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkObjectsPreChecked() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref networkObject); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkObject))); + serializer.SerializeValuePreChecked(ref networkObject); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkObject readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(networkObject, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkBehavioursPreChecked() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref networkBehaviour); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkBehaviour))); + serializer.SerializeValuePreChecked(ref networkBehaviour); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkBehaviour readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(networkBehaviour, readValue); + } + } + } + ); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta new file mode 100644 index 0000000000..cb62c7e2d2 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cf899c97866c76498b71585a61a8142 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From a3e0abcc119beeb707558409b2eca71175b96d2a Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 23 Aug 2021 17:29:51 -0500 Subject: [PATCH 08/58] Removed unnecessary comment. --- .../Editor/Serialization/BufferSerializerTests.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 6ea498b5dd..4c6f967c00 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -390,18 +390,6 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) } } } - /* - public void SerializeValue(ref object value, Type type, bool isNullable = false); - public void SerializeValue(ref INetworkSerializable value); - public void SerializeValue(ref GameObject value); - public void SerializeValue(ref NetworkObject value); - public void SerializeValue(ref NetworkBehaviour value); - - public bool PreCheck(int amount); - public void SerializeValuePreChecked(ref GameObject value); - public void SerializeValuePreChecked(ref NetworkObject value); - public void SerializeValuePreChecked(ref NetworkBehaviour value);*/ - private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, NetworkObject networkObject); From ea31f101610867af216d209d8734cbbc31d3aeae Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 23 Aug 2021 22:24:33 -0500 Subject: [PATCH 09/58] XMLDocs + cleanup for PR --- .../Runtime/Serialization/BitCounter.cs | 88 ++-- .../Runtime/Serialization/BitReader.cs | 78 +-- .../Runtime/Serialization/BitWriter.cs | 91 ++-- .../Runtime/Serialization/BufferSerializer.cs | 226 ++++++++- .../Serialization/BufferSerializerReader.cs | 2 +- .../Serialization/BufferSerializerWriter.cs | 2 +- .../Runtime/Serialization/BytePacker.cs | 173 +++++-- .../Runtime/Serialization/ByteUnpacker.cs | 187 +++++--- .../Runtime/Serialization/BytewiseUtility.cs | 57 +++ ...alizer.cs.meta => BytewiseUtility.cs.meta} | 2 +- .../Runtime/Serialization/FastBufferReader.cs | 263 ++++++++--- .../Runtime/Serialization/FastBufferWriter.cs | 445 ++++++++++++------ ....cs => IBufferSerializerImplementation.cs} | 0 .../IBufferSerializerImplementation.cs.meta | 11 + .../Serialization/SerializationTypeTable.cs | 11 + 15 files changed, 1154 insertions(+), 482 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs rename com.unity.netcode.gameobjects/Runtime/Serialization/{IBufferSerializer.cs.meta => BytewiseUtility.cs.meta} (83%) rename com.unity.netcode.gameobjects/Runtime/Serialization/{IBufferSerializer.cs => IBufferSerializerImplementation.cs} (100%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs index a357077bb5..44c09dc8cf 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -53,54 +53,74 @@ public static class BitCounter 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, }; + /// + /// Get the minimum number of bytes required to represent the given value + /// + /// The value + /// The number of bytes required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedByteCount(uint b) + public static int GetUsedByteCount(uint value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b = b & ~(b >> 1); - return k_deBruijnTableBytes32[b*k_DeBruijnMagic32 >> 27]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value = value & ~(value >> 1); + return k_deBruijnTableBytes32[value*k_DeBruijnMagic32 >> 27]; } + /// + /// Get the minimum number of bytes required to represent the given value + /// + /// The value + /// The number of bytes required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedByteCount(ulong b) + public static int GetUsedByteCount(ulong value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b |= b >> 32; - b = b & ~(b >> 1); - return k_deBruijnTableBytes64[b*k_DeBruijnMagic64 >> 58]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + value = value & ~(value >> 1); + return k_deBruijnTableBytes64[value*k_DeBruijnMagic64 >> 58]; } + /// + /// Get the minimum number of bits required to represent the given value + /// + /// The value + /// The number of bits required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedBitCount(uint b) + public static int GetUsedBitCount(uint value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b = b & ~(b >> 1); - return k_deBruijnTableBits32[b*k_DeBruijnMagic32 >> 27]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value = value & ~(value >> 1); + return k_deBruijnTableBits32[value*k_DeBruijnMagic32 >> 27]; } + /// + /// Get the minimum number of bits required to represent the given value + /// + /// The value + /// The number of bits required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedBitCount(ulong b) + public static int GetUsedBitCount(ulong value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b |= b >> 32; - b = b & ~(b >> 1); - return k_deBruijnTableBits64[b*k_DeBruijnMagic64 >> 58]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + value = value & ~(value >> 1); + return k_deBruijnTableBits64[value*k_DeBruijnMagic64 >> 58]; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 5cda1a6bf6..7c564c6d0a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,16 +1,19 @@ using System; using System.Runtime.CompilerServices; -using Mono.Cecil; -using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Multiplayer.Netcode { + /// + /// Helper class for doing bitwise reads for a FastBufferReader. + /// Ensures all bitwise reads end on proper byte alignment so FastBufferReader doesn't have to be concerned + /// with misaligned reads. + /// public ref struct BitReader { private Ref m_Reader; - private unsafe byte* m_BufferPointer; - private int m_Position; + private readonly unsafe byte* m_BufferPointer; + private readonly int m_Position; private const int BITS_PER_BYTE = 8; private int m_BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -38,7 +41,10 @@ internal unsafe BitReader(ref FastBufferReader reader) #endif } - public unsafe void Dispose() + /// + /// Pads the read bit count to byte alignment and commits the read back to the reader + /// + public void Dispose() { var bytesWritten = m_BitPosition >> 3; if (!BitAligned) @@ -50,7 +56,15 @@ public unsafe void Dispose() m_Reader.Value.CommitBitwiseReads(bytesWritten); } - public unsafe bool VerifyCanReadBits(int bitCount) + /// + /// Verifies the requested bit count can be read from the buffer. + /// This exists as a separate method to allow multiple bit reads to be bounds checked with a single call. + /// If it returns false, you may not read, and in editor and development builds, attempting to do so will + /// throw an exception. In release builds, attempting to do so will read junk memory. + /// + /// Number of bits you want to read, in total + /// True if you can read, false if that would exceed buffer bounds + public bool VerifyCanReadBits(int bitCount) { var newBitPosition = m_BitPosition + bitCount; var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; @@ -122,7 +136,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) /// /// Value to store bits into. /// Amount of bits to read. - public unsafe void ReadBits(out byte value, int bitCount) + public void ReadBits(out byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR int checkPos = (m_BitPosition + bitCount); @@ -156,59 +170,17 @@ public unsafe void ReadBit(out bool bit) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + private unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged { - // Switch statement to read small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToRead) - { - case 1: - ptr[0] = *bufferPointer; - break; - case 2: - *(ushort*) ptr = *(ushort*)bufferPointer; - break; - case 3: - *(ushort*) ptr = *(ushort*)bufferPointer; - *(ptr+2) = *(bufferPointer+2); - break; - case 4: - *(uint*) ptr = *(uint*)bufferPointer; - break; - case 5: - *(uint*) ptr = *(uint*)bufferPointer; - *(ptr+4) = *(bufferPointer+4); - break; - case 6: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - break; - case 7: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - *(ptr+6) = *(bufferPointer+6); - break; - case 8: - *(ulong*) ptr = *(ulong*)bufferPointer; - break; - default: - UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); - break; - } + BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; } - /// - /// Read a certain amount of bits from the stream. - /// - /// How many bits to read. Minimum 0, maximum 64. - /// The bits that were read private byte ReadByteBits(int bitCount) { if (bitCount > 8) @@ -235,11 +207,11 @@ private byte ReadByteBits(int bitCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void ReadMisaligned(out byte value) { - int off = (int)(m_BitPosition & 7); + int off = m_BitPosition & 7; int pos = m_BitPosition >> 3; int shift1 = 8 - off; - value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_BitPosition += 8) >> 3] << shift1)); + value = (byte)((m_BufferPointer[pos] >> shift1) | (m_BufferPointer[(m_BitPosition += 8) >> 3] << shift1)); } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 1af6025a90..5bbf095641 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,17 +1,21 @@ using System; using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { + /// + /// Helper class for doing bitwise writes for a FastBufferWriter. + /// Ensures all bitwise writes end on proper byte alignment so FastBufferWriter doesn't have to be concerned + /// with misaligned writes. + /// public ref struct BitWriter { private Ref m_Writer; private unsafe byte* m_BufferPointer; - private int m_Position; - internal int m_BitPosition; + private readonly int m_Position; + private int m_BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal int m_AllowedBitwiseWriteMark; + private int m_AllowedBitwiseWriteMark; #endif private const int BITS_PER_BYTE = 8; @@ -23,8 +27,7 @@ public bool BitAligned [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (m_BitPosition & 7) == 0; } - - + internal unsafe BitWriter(ref FastBufferWriter writer) { m_Writer = new Ref(ref writer); @@ -35,7 +38,31 @@ internal unsafe BitWriter(ref FastBufferWriter writer) m_AllowedBitwiseWriteMark = (m_Writer.Value.m_AllowedWriteMark - m_Writer.Value.Position) * BITS_PER_BYTE; #endif } + + /// + /// Pads the written bit count to byte alignment and commits the write back to the writer + /// + public void Dispose() + { + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned) + { + // Accounting for the partial write + ++bytesWritten; + } + + m_Writer.Value.CommitBitwiseWrites(bytesWritten); + } + /// + /// Verifies the requested bit count can be written to the buffer. + /// This exists as a separate method to allow multiple bit writes to be bounds checked with a single call. + /// If it returns false, you may not write, and in editor and development builds, attempting to do so will + /// throw an exception. In release builds, attempting to do so will write to random memory addresses and cause + /// Bad Things(TM). + /// + /// Number of bits you want to write, in total + /// True if you can write, false if that would exceed buffer bounds public unsafe bool VerifyCanWriteBits(int bitCount) { var newBitPosition = m_BitPosition + bitCount; @@ -64,18 +91,6 @@ public unsafe bool VerifyCanWriteBits(int bitCount) return true; } - public unsafe void Dispose() - { - var bytesWritten = m_BitPosition >> 3; - if (!BitAligned) - { - // Accounting for the partial write - ++bytesWritten; - } - - m_Writer.Value.CommitBitwiseWrites(bytesWritten); - } - /// /// Write s certain amount of bits to the stream. /// @@ -169,47 +184,9 @@ public unsafe void WriteBit(bool bit) [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToWrite) - { - case 1: - bufferPointer[0] = *ptr; - break; - case 2: - *(ushort*) bufferPointer = *(ushort*)ptr; - break; - case 3: - *(ushort*) bufferPointer = *(ushort*)ptr; - *(bufferPointer+2) = *(ptr+2); - break; - case 4: - *(uint*) bufferPointer = *(uint*)ptr; - break; - case 5: - *(uint*) bufferPointer = *(uint*)ptr; - *(bufferPointer+4) = *(ptr+4); - break; - case 6: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - break; - case 7: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - *(bufferPointer+6) = *(ptr+6); - break; - case 8: - *(ulong*) bufferPointer = *(ulong*)ptr; - break; - default: - UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); - break; - } + BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * BITS_PER_BYTE; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index 5e409704e9..cfe11aed19 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -4,99 +4,307 @@ namespace Unity.Netcode.Serialization { - public ref struct BufferSerializer where T : IBufferSerializerImplementation + /// + /// Two-way serializer wrapping FastBufferReader or FastBufferWriter. + /// + /// Implemented as a ref struct for two reasons: + /// 1. The BufferSerializer cannot outlive the FBR/FBW it wraps or using it will cause a crash + /// 2. The BufferSerializer must always be passed by reference and can't be copied + /// + /// Ref structs help enforce both of those rules: they can't out live the stack context in which they were + /// created, and they're always passed by reference no matter what. + /// + /// BufferSerializer doesn't wrapp FastBufferReader or FastBufferWriter directly because it can't. + /// ref structs can't implement interfaces, and in order to be able to have two different implementations with + /// the same interface (which allows us to avoid an "if(IsReader)" on every call), the thing directly wrapping + /// the struct has to implement an interface. So IBufferSerializerImplementation exists as the interface, + /// which is implemented by a normal struct, while the ref struct wraps the normal one to enforce the two above + /// requirements. (Allowing direct access to the IBufferSerializerImplementation struct would allow dangerous + /// things to happen because the struct's lifetime could outlive the Reader/Writer's.) + /// + /// The implementation struct + public ref struct BufferSerializer where TImplementation : IBufferSerializerImplementation { - private T m_Implementation; + private TImplementation m_Implementation; + /// + /// Check if the contained implementation is a reader + /// public bool IsReader => m_Implementation.IsReader; + + /// + /// Check if the contained implementation is a writer + /// public bool IsWriter => m_Implementation.IsWriter; - public BufferSerializer(T implementation) + public BufferSerializer(TImplementation implementation) { m_Implementation = implementation; } + /// + /// Retrieves the FastBufferReader instance. Only valid if IsReader = true, throws + /// InvalidOperationException otherwise. + /// + /// Reader instance public ref FastBufferReader GetFastBufferReader() { return ref m_Implementation.GetFastBufferReader(); } + + /// + /// Retrieves the FastBufferWriter instance. Only valid if IsWriter = true, throws + /// InvalidOperationException otherwise. + /// + /// Writer instance public ref FastBufferWriter GetFastBufferWriter() { return ref m_Implementation.GetFastBufferWriter(); } + /// + /// Serialize an object value. + /// Note: Will ALWAYS cause allocations when reading. + /// This function is also much slower than the others as it has to figure out how to serialize + /// the object using runtime reflection. + /// It's recommended not to use this unless you have no choice. + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize + /// Type to deserialize to when reading + /// + /// If true, will force an isNull byte to be written. + /// Some types will write this byte regardless. + /// public void SerializeValue(ref object value, Type type, bool isNullable = false) { m_Implementation.SerializeValue(ref value, type, isNullable); } + + /// + /// Serialize an INetworkSerializable + /// If your INetworkSerializable is implemented by a struct, as opposed to a class, use this + /// function instead of SerializeValue. SerializeValue will incur a boxing allocation, + /// SerializeNetworkSerializable will not. + /// + /// A definition of SerializeValue that doesn't allocate can't be created because C# + /// doesn't allow overriding generics based solely on the constraint, so this would conflict + /// with WriteValue<T>(ref T value) where T: unmanaged + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Implementation.SerializeNetworkSerializable(ref value); + } + + /// + /// Serialize an INetworkSerializable + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref INetworkSerializable value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a GameObject + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref GameObject value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a NetworkObject + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref NetworkObject value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a NetworkBehaviour + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref NetworkBehaviour value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a string. + /// + /// Note: Will ALWAYS allocate a new string when reading. + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize + /// + /// If true, will truncate each char to one byte. + /// This is slower than two-byte chars, but uses less bandwidth. + /// public void SerializeValue(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValue(ref s, oneByteChars); } + + /// + /// Serialize an array value. + /// + /// Note: Will ALWAYS allocate a new array when reading. + /// If you have a statically-sized array that you know is large enough, it's recommended to + /// serialize the size yourself and iterate serializing array members. + /// + /// (This is because C# doesn't allow setting an array's length value, so deserializing + /// into an existing array of larger size would result in an array that doesn't have as many values + /// as its Length indicates it should.) + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref T[] array) where T : unmanaged { m_Implementation.SerializeValue(ref array); } + + /// + /// Serialize a single byte + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref byte value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize an unmanaged type. Supports basic value types as well as structs. + /// The provided type will be copied to/from the buffer as it exists in memory. + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref T value) where T : unmanaged { m_Implementation.SerializeValue(ref value); } - - // Has to have a different name to avoid conflicting with "where T: unmananged" - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable - { - m_Implementation.SerializeNetworkSerializable(ref value); - } + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call PreCheck() once on the total size, and then follow it with calls to + /// SerializeValuePreChecked() for faster serialization. Write buffers will grow during PreCheck() + /// if needed. + /// + /// PreChecked serialization operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using PreCheck(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using PreCheck is to avoid bounds checking in the following + /// operations in release builds. + /// + /// Number of bytes you plan to read or write + /// True if the read/write can proceed, false otherwise. public bool PreCheck(int amount) { return m_Implementation.PreCheck(amount); } + /// + /// Serialize a GameObject + /// + /// Value to serialize public void SerializeValuePreChecked(ref GameObject value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize a NetworkObject + /// + /// Value to serialize public void SerializeValuePreChecked(ref NetworkObject value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize a NetworkBehaviour + /// + /// Value to serialize public void SerializeValuePreChecked(ref NetworkBehaviour value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize a string. + /// + /// Note: Will ALWAYS allocate a new string when reading. + /// + /// Value to serialize + /// + /// If true, will truncate each char to one byte. + /// This is slower than two-byte chars, but uses less bandwidth. + /// public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); } + + /// + /// Serialize an array value. + /// + /// Note: Will ALWAYS allocate a new array when reading. + /// If you have a statically-sized array that you know is large enough, it's recommended to + /// serialize the size yourself and iterate serializing array members. + /// + /// (This is because C# doesn't allow setting an array's length value, so deserializing + /// into an existing array of larger size would result in an array that doesn't have as many values + /// as its Length indicates it should.) + /// + /// Value to serialize public void SerializeValuePreChecked(ref T[] array) where T : unmanaged { m_Implementation.SerializeValuePreChecked(ref array); } + + /// + /// Serialize a single byte + /// + /// Value to serialize public void SerializeValuePreChecked(ref byte value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize an unmanaged type. Supports basic value types as well as structs. + /// The provided type will be copied to/from the buffer as it exists in memory. + /// + /// Value to serialize public void SerializeValuePreChecked(ref T value) where T : unmanaged { m_Implementation.SerializeValuePreChecked(ref value); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index 7016806da9..e809cec933 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -4,7 +4,7 @@ namespace Unity.Netcode.Serialization { - public struct BufferSerializerReader : IBufferSerializerImplementation + internal struct BufferSerializerReader : IBufferSerializerImplementation { private Ref m_Reader; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 2b16b718fa..916c0f51ef 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -4,7 +4,7 @@ namespace Unity.Netcode.Serialization { - public struct BufferSerializerWriter : IBufferSerializerImplementation + internal struct BufferSerializerWriter : IBufferSerializerImplementation { private Ref m_Writer; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 3976032578..f935ad400d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -5,14 +5,24 @@ namespace Unity.Multiplayer.Netcode { + /// + /// Utility class for packing values in serialization. + /// public static class BytePacker { #region Managed TypePacking + /// /// Writes a boxed object in a packed format - /// Named differently from other WriteValuePacked methods to avoid accidental boxing + /// Named differently from other WriteValuePacked methods to avoid accidental boxing. + /// Don't use this method unless you have no other choice. /// + /// Writer to write to /// The object to write + /// + /// If true, an extra byte will be written to indicate whether or not the value is null. + /// Some types will always write this. + /// public static void WriteObjectPacked(ref FastBufferWriter writer, object value, bool isNullable = false) { #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -21,7 +31,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, #endif if (isNullable || value.GetType().IsNullable()) { - bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + bool isNull = value == null || (value is UnityEngine.Object o && o == null); WriteValuePacked(ref writer, isNull); @@ -140,6 +150,12 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValuePacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else + /// + /// Write a packed enum value. + /// + /// The writer to write to + /// The value to write + /// An enum type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteValuePacked(ref FastBufferWriter writer, TEnum value) where TEnum : unmanaged, Enum { @@ -162,8 +178,9 @@ public static unsafe void WriteValuePacked(ref FastBufferWriter writer, T } /// - /// Write single-precision floating point value to the stream as a varint + /// Write single-precision floating point value to the buffer as a varint /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, float value) @@ -172,8 +189,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, float value) } /// - /// Write double-precision floating point value to the stream as a varint + /// Write double-precision floating point value to the buffer as a varint /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, double value) @@ -182,97 +200,99 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) } /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Write a byte to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Write a signed byte to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, sbyte value) => writer.WriteByteSafe((byte)value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Write a bool to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValueSafe(value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// Write a signed short (Int16) as a ZigZag encoded varint to the buffer. /// WARNING: If the value you're writing is > 2287, this will use MORE space /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. /// Only use this if you're certain your value will be small. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, short value) => WriteUInt32Packed(ref writer, (ushort)Arithmetic.ZigZagEncode(value)); /// - /// Write an unsigned short (UInt16) as a varint to the stream. + /// Write an unsigned short (UInt16) as a varint to the buffer. /// WARNING: If the value you're writing is > 2287, this will use MORE space /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. /// Only use this if you're certain your value will be small. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, ushort value) => WriteUInt32Packed(ref writer, value); /// - /// Write a two-byte character as a varint to the stream. + /// Write a two-byte character as a varint to the buffer. /// WARNING: If the value you're writing is > 2287, this will use MORE space /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. /// Only use this if you're certain your value will be small. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, char c) => WriteUInt32Packed(ref writer, c); /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// Write a signed int (Int32) as a ZigZag encoded varint to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, int value) => WriteUInt32Packed(ref writer, (uint)Arithmetic.ZigZagEncode(value)); /// - /// Write an unsigned int (UInt32) to the stream. + /// Write an unsigned int (UInt32) to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, uint value) => WriteUInt32Packed(ref writer, value); /// - /// Write an unsigned long (UInt64) to the stream. + /// Write an unsigned long (UInt64) to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, ulong value) => WriteUInt64Packed(ref writer, value); /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// Write a signed long (Int64) as a ZigZag encoded varint to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteValuePacked(ref FastBufferWriter writer, long value) => WriteUInt64Packed(ref writer, (ulong)Arithmetic.ZigZagEncode(value)); + public static void WriteValuePacked(ref FastBufferWriter writer, long value) => WriteUInt64Packed(ref writer, Arithmetic.ZigZagEncode(value)); /// - /// Convenience method that writes two packed Vector3 from the ray to the stream + /// Convenience method that writes two packed Vector3 from the ray to the buffer /// + /// The writer to write to /// Ray to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Ray ray) @@ -282,8 +302,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Ray ray) } /// - /// Convenience method that writes two packed Vector2 from the ray to the stream + /// Convenience method that writes two packed Vector2 from the ray to the buffer /// + /// The writer to write to /// Ray2D to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Ray2D ray2d) @@ -293,8 +314,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Ray2D ray2d) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that writes four varint floats from the color to the buffer /// + /// The writer to write to /// Color to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Color color) @@ -306,8 +328,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Color color) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that writes four varint floats from the color to the buffer /// + /// The writer to write to /// Color to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Color32 color) @@ -319,8 +342,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Color32 color) } /// - /// Convenience method that writes two varint floats from the vector to the stream + /// Convenience method that writes two varint floats from the vector to the buffer /// + /// The writer to write to /// Vector to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Vector2 vector2) @@ -330,8 +354,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector2 vector2 } /// - /// Convenience method that writes three varint floats from the vector to the stream + /// Convenience method that writes three varint floats from the vector to the buffer /// + /// The writer to write to /// Vector to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Vector3 vector3) @@ -342,8 +367,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector3 vector3 } /// - /// Convenience method that writes four varint floats from the vector to the stream + /// Convenience method that writes four varint floats from the vector to the buffer /// + /// The writer to write to /// Vector to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4) @@ -355,8 +381,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4 } /// - /// Writes the rotation to the stream. + /// Writes the rotation to the buffer. /// + /// The writer to write to /// Rotation to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rotation) @@ -370,7 +397,8 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rota /// /// Writes a string in a packed format /// - /// + /// The writer to write to + /// The value to pack [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, string s) { @@ -391,8 +419,29 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueBitPacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else + /// + /// Writes a 14-bit signed short to the buffer in a bit-encoded packed format. + /// The first bit indicates whether the value is 1 byte or 2. + /// The sign bit takes up another bit. + /// That leaves 14 bits for the value. + /// A value greater than 2^14-1 or less than -2^14 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its two + /// most significant bits after zig-zag encoding. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 15-bit unsigned short to the buffer in a bit-encoded packed format. + /// The first bit indicates whether the value is 1 byte or 2. + /// That leaves 15 bits for the value. + /// A value greater than 2^15-1 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its + /// most significant bit. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -419,8 +468,29 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value writer.WriteValue((ushort)((value << 1) | 0b1)); } + /// + /// Writes a 29-bit signed int to the buffer in a bit-encoded packed format. + /// The first two bits indicate whether the value is 1, 2, 3, or 4 bytes. + /// The sign bit takes up another bit. + /// That leaves 29 bits for the value. + /// A value greater than 2^29-1 or less than -2^29 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its three + /// most significant bits after zig-zag encoding. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint) Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 30-bit unsigned int to the buffer in a bit-encoded packed format. + /// The first two bits indicate whether the value is 1, 2, 3, or 4 bytes. + /// That leaves 30 bits for the value. + /// A value greater than 2^30-1 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its two + /// most significant bits. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -438,8 +508,29 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } - public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, (ulong) Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 60-bit signed long to the buffer in a bit-encoded packed format. + /// The first three bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, or 8 bytes. + /// The sign bit takes up another bit. + /// That leaves 60 bits for the value. + /// A value greater than 2^60-1 or less than -2^60 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its four + /// most significant bits after zig-zag encoding. + /// + /// The writer to write to + /// The value to pack + public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 61-bit unsigned long to the buffer in a bit-encoded packed format. + /// The first three bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, or 8 bytes. + /// That leaves 31 bits for the value. + /// A value greater than 2^61-1 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its three + /// most significant bits. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -522,20 +613,6 @@ private static unsafe ulong ToUlong(T value) where T : unmanaged ulong* asUlong = (ulong*) &value; return *asUlong; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe float ToSingle(T value) where T : unmanaged - { - float* asFloat = (float*) &value; - return *asFloat; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe double ToDouble(T value) where T : unmanaged - { - double* asDouble = (double*) &value; - return *asDouble; - } #endregion } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index 5ac013acec..dc827b40b6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -8,11 +8,19 @@ namespace Unity.Multiplayer.Netcode public static class ByteUnpacker { #region Managed TypePacking + /// - /// Writes a boxed object in a packed format + /// Reads a boxed object in a packed format /// Named differently from other ReadValuePacked methods to avoid accidental boxing + /// Don't use this method unless you have no other choice. /// - /// The object to write + /// The reader to read from + /// The object to read + /// The type of the object to read (i.e., typeof(int)) + /// + /// If true, reads a byte indicating whether or not the object is null. + /// Should match the way the object was written. + /// public static void ReadObjectPacked(ref FastBufferReader reader, out object value, Type type, bool isNullable = false) { #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -162,11 +170,12 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, ou throw new InvalidOperationException("Enum is a size that cannot exist?!"); } } - + /// - /// Write single-precision floating point value to the stream as a varint + /// Read single-precision floating point value from the stream as a varint /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out float value) { @@ -175,9 +184,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out float value) } /// - /// Write double-precision floating point value to the stream as a varint + /// Read double-precision floating point value from the stream as a varint /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out double value) { @@ -186,22 +196,18 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value } /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a byte from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByteSafe(out value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a signed byte from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) { @@ -210,23 +216,19 @@ public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) } /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a boolean from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValueSafe(out value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read an usigned short (Int16) as a varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out short value) { @@ -235,12 +237,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out short value) } /// - /// Write an unsigned short (UInt16) as a varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read an unsigned short (UInt16) as a varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out ushort value) { @@ -249,12 +249,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out ushort value } /// - /// Write a two-byte character as a varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a two-byte character as a varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out char c) { @@ -263,9 +261,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out char c) } /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// Read a signed int (Int32) as a ZigZag encoded varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out int value) { @@ -274,23 +273,26 @@ public static void ReadValuePacked(ref FastBufferReader reader, out int value) } /// - /// Write an unsigned int (UInt32) to the stream. + /// Read an unsigned int (UInt32) from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out uint value) => ReadUInt32Packed(ref reader, out value); /// - /// Write an unsigned long (UInt64) to the stream. + /// Read an unsigned long (UInt64) from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out ulong value) => ReadUInt64Packed(ref reader, out value); /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// Read a signed long (Int64) as a ZigZag encoded varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out long value) { @@ -299,9 +301,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out long value) } /// - /// Convenience method that writes two packed Vector3 from the ray to the stream + /// Convenience method that reads two packed Vector3 from the ray from the stream /// - /// Ray to write + /// The reader to read from + /// Ray to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Ray ray) { @@ -311,9 +314,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Ray ray) } /// - /// Convenience method that writes two packed Vector2 from the ray to the stream + /// Convenience method that reads two packed Vector2 from the ray from the stream /// - /// Ray2D to write + /// The reader to read from + /// Ray2D to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Ray2D ray2d) { @@ -323,9 +327,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Ray2D ray2d) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that reads four varint floats from the color from the stream /// - /// Color to write + /// The reader to read from + /// Color to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Color color) { @@ -337,9 +342,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Color color) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that reads four varint floats from the color from the stream /// - /// Color to write + /// The reader to read from + /// Color to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Color32 color) { @@ -351,9 +357,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Color32 colo } /// - /// Convenience method that writes two varint floats from the vector to the stream + /// Convenience method that reads two varint floats from the vector from the stream /// - /// Vector to write + /// The reader to read from + /// Vector to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Vector2 vector2) { @@ -363,9 +370,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Vector2 vect } /// - /// Convenience method that writes three varint floats from the vector to the stream + /// Convenience method that reads three varint floats from the vector from the stream /// - /// Vector to write + /// The reader to read from + /// Vector to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Vector3 vector3) { @@ -376,9 +384,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Vector3 vect } /// - /// Convenience method that writes four varint floats from the vector to the stream + /// Convenience method that reads four varint floats from the vector from the stream /// - /// Vector to write + /// The reader to read from + /// Vector to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Vector4 vector4) { @@ -390,9 +399,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Vector4 vect } /// - /// Writes the rotation to the stream. + /// Reads the rotation from the stream. /// - /// Rotation to write + /// The reader to read from + /// Rotation to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion rotation) { @@ -404,8 +414,9 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion r } /// - /// Writes a string in a packed format + /// Reads a string in a packed format /// + /// The reader to read from /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void ReadValuePacked(ref FastBufferReader reader, out string s) @@ -431,12 +442,24 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, out strin [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueBitPacked(ref FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value); #else + /// + /// Read a bit-packed 14-bit signed short from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static void ReadValueBitPacked(ref FastBufferReader reader, out short value) { ReadValueBitPacked(ref reader, out ushort readValue); value = (short)Arithmetic.ZigZagDecode(readValue); } + /// + /// Read a bit-packed 15-bit unsigned short from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ushort value) { ushort returnValue = 0; @@ -463,11 +486,24 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us value = (ushort)(returnValue >> 1); } + /// + /// Read a bit-packed 29-bit signed int from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static void ReadValueBitPacked(ref FastBufferReader reader, out int value) { ReadValueBitPacked(ref reader, out uint readValue); value = (int)Arithmetic.ZigZagDecode(readValue); } + + /// + /// Read a bit-packed 30-bit unsigned int from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out uint value) { uint returnValue = 0; @@ -499,11 +535,24 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui value = returnValue >> 2; } + /// + /// Read a bit-packed 60-bit signed long from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static void ReadValueBitPacked(ref FastBufferReader reader, out long value) { ReadValueBitPacked(ref reader, out ulong readValue); value = Arithmetic.ZigZagDecode(readValue); } + + /// + /// Read a bit-packed 61-bit signed long from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ulong value) { ulong returnValue = 0; @@ -602,20 +651,6 @@ private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value reader.ReadPartialValue(out value, numBytes); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe uint ToUint(T value) where T : unmanaged - { - uint* asUint = (uint*) &value; - return *asUint; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe ulong ToUlong(T value) where T : unmanaged - { - ulong* asUlong = (ulong*) &value; - return *asUlong; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe float ToSingle(T value) where T : unmanaged { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs new file mode 100644 index 0000000000..de1e617069 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -0,0 +1,57 @@ +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Multiplayer.Netcode +{ + public static class BytewiseUtility + { + /// + /// Helper function optimized for quickly copying small numbers of bytes. + /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 + /// Slower for amount > 8 + /// + /// Pointer to the source value + /// Pointer to the destination value + /// Number of bytes to copy + public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + switch (amount) + { + case 1: + dest[0] = *source; + break; + case 2: + *(ushort*) dest = *(ushort*)source; + break; + case 3: + *(ushort*) dest = *(ushort*)source; + *(dest+2) = *(source+2); + break; + case 4: + *(uint*) dest = *(uint*)source; + break; + case 5: + *(uint*) dest = *(uint*)source; + *(dest+4) = *(source+4); + break; + case 6: + *(uint*) dest = *(uint*)source; + *(ushort*) (dest+4) = *(ushort*)(source+4); + break; + case 7: + *(uint*) dest = *(uint*)source; + *(ushort*) (dest+4) = *(ushort*)(source+4); + *(dest+6) = *(source+6); + break; + case 8: + *(ulong*) dest = *(ulong*)source; + break; + default: + UnsafeUtility.MemCpy(dest, source, amount); + break; + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta index 6dd5e01fcc..12bebf3d01 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e09b1183d63526341acdacfebdad750d +guid: 5df078ced492f8c45966997ceda09c8f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index ff9254f9a8..ac571ad9c3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -9,14 +9,32 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferReader : IDisposable { - internal unsafe byte* m_BufferPointer; + internal readonly unsafe byte* m_BufferPointer; internal int m_Position; - internal int m_Length; - internal Allocator m_Allocator; + internal readonly int m_Length; + private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR internal int m_AllowedReadMark; - internal bool m_InBitwiseContext; + private bool m_InBitwiseContext; #endif + + /// + /// Get the current read position + /// + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + /// + /// Get the total length of the buffer + /// + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Length; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseReads(int amount) @@ -41,9 +59,18 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in #endif } + /// + /// Create a FastBufferReader from an ArraySegment. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The buffer to copy from + /// The allocator to use + /// The number of bytes to copy (all if this is -1) + /// The offset of the buffer to start copying from public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? buffer.Count : length); + m_Length = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer.Array) { @@ -58,9 +85,18 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i #endif } + /// + /// Create a FastBufferReader from an existing byte array. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The buffer to copy from + /// The allocator to use + /// The number of bytes to copy (all if this is -1) + /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? buffer.Length : length); + m_Length = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer) { @@ -75,6 +111,15 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = #endif } + /// + /// Create a FastBufferReader from an existing byte buffer. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The buffer to copy from + /// The allocator to use + /// The number of bytes to copy + /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) { m_Length = Math.Max(1, length); @@ -89,6 +134,15 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in #endif } + /// + /// Create a FastBufferReader from a FastBufferWriter. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The writer to copy from + /// The allocator to use + /// The number of bytes to copy (all if this is -1) + /// The offset of the buffer to start copying from public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0) { m_Length = Math.Max(1, length == -1 ? writer.Length : length); @@ -103,17 +157,30 @@ public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, #endif } + /// + /// Frees the allocated buffer + /// public unsafe void Dispose() { UnsafeUtility.Free(m_BufferPointer, m_Allocator); } + /// + /// Move the read position in the stream + /// + /// Absolute value to move the position to, truncated to Length [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { m_Position = Math.Min(Length, where); } + /// + /// Mark that some bytes are going to be read via GetUnsafePtr(). + /// + /// Amount that will be read + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void MarkBytesRead(int amount) { @@ -131,6 +198,12 @@ internal void MarkBytesRead(int amount) m_Position += amount; } + /// + /// Retrieve a BitReader to be able to perform bitwise operations on the buffer. + /// No bytewise operations can be performed on the buffer until bitReader.Dispose() has been called. + /// At the end of the operation, FastBufferReader will remain byte-aligned. + /// + /// A BitReader public BitReader EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -139,21 +212,22 @@ public BitReader EnterBitwiseContext() return new BitReader(ref this); } - public int Position - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; - } - - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Length; - } - - + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be reading multiple fields back-to-back and you know the total size, + /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// ReadValue() instead of ReadValueSafe() for faster serialization. + /// + /// Unsafe read operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// operations in release builds. + /// + /// Amount of bytes to read + /// True if the read is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool VerifyCanReadInternal(int bytes) + public bool VerifyCanRead(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -167,16 +241,27 @@ internal bool VerifyCanReadInternal(int bytes) return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedReadMark) - { - m_AllowedReadMark = m_Position + bytes; - } + m_AllowedReadMark = m_Position + bytes; #endif return true; } + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be reading multiple fields back-to-back and you know the total size, + /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// ReadValue() instead of ReadValueSafe() for faster serialization. + /// + /// Unsafe read operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// operations in release builds. + /// + /// The value you want to read + /// True if the read is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanRead(int bytes) + public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -185,18 +270,26 @@ public bool VerifyCanRead(int bytes) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Length) + int len = sizeof(T); + if (m_Position + len > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + bytes; + m_AllowedReadMark = m_Position + len; #endif return true; } + /// + /// Internal version of VerifyCanRead. + /// Differs from VerifyCanRead only in that it won't ever move the AllowedReadMark backward. + /// + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged + internal bool VerifyCanReadInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -205,17 +298,24 @@ public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - int len = sizeof(T); - if (m_Position + len > m_Length) + if (m_Position + bytes > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + len; + if (m_Position + bytes > m_AllowedReadMark) + { + m_AllowedReadMark = m_Position + bytes; + } #endif return true; } + /// + /// Returns an array representation of the underlying byte buffer. + /// !!Allocates a new array!! + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte[] ToArray() { @@ -227,12 +327,20 @@ public unsafe byte[] ToArray() return ret; } + /// + /// Gets a direct pointer to the underlying buffer + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { return m_BufferPointer; } + /// + /// Gets a direct pointer to the underlying buffer at the current read position + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { @@ -245,7 +353,10 @@ public unsafe byte[] ToArray() /// /// The object to read /// The type to be read - /// Should a null value be encoded? Any type for which type.IsNullable() returns true will always encode it. + /// + /// If true, reads a byte indicating whether or not the object is null. + /// Should match the way the object was written. + /// public void ReadObject(out object value, Type type, bool isNullable = false) { if (isNullable || type.IsNullable()) @@ -358,11 +469,21 @@ public void ReadObject(out object value, Type type, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } + /// + /// Read an INetworkSerializable + /// + /// INetworkSerializable instance + /// + /// public void ReadNetworkSerializable(out T value) where T : INetworkSerializable { throw new NotImplementedException(); } + /// + /// Read a GameObject + /// + /// value to read public void ReadValue(out GameObject value) { ReadValue(out ulong networkObjectId); @@ -381,6 +502,13 @@ public void ReadValue(out GameObject value) value = null; } + /// + /// Read a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// value to read public void ReadValueSafe(out GameObject value) { ReadValueSafe(out ulong networkObjectId); @@ -399,6 +527,10 @@ public void ReadValueSafe(out GameObject value) value = null; } + /// + /// Read a NetworkObject + /// + /// value to read public void ReadValue(out NetworkObject value) { ReadValue(out ulong networkObjectId); @@ -417,6 +549,13 @@ public void ReadValue(out NetworkObject value) value = null; } + /// + /// Read a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// value to read public void ReadValueSafe(out NetworkObject value) { ReadValueSafe(out ulong networkObjectId); @@ -435,6 +574,10 @@ public void ReadValueSafe(out NetworkObject value) value = null; } + /// + /// Read a NetworkBehaviour + /// + /// value to read public void ReadValue(out NetworkBehaviour value) { ReadValue(out ulong networkObjectId); @@ -454,6 +597,13 @@ public void ReadValue(out NetworkBehaviour value) value = null; } + /// + /// Read a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// value to read public void ReadValueSafe(out NetworkBehaviour value) { ReadValueSafe(out ulong networkObjectId); @@ -605,13 +755,18 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged } } + /// + /// Read a partial value. The value is zero-initialized and then the specified number of bytes is read into it. + /// + /// Value to read + /// Number of bytes + /// Offset into the value to write the bytes + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged { - // Switch statement to read small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -627,41 +782,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToRead) - { - case 1: - ptr[0] = *bufferPointer; - break; - case 2: - *(ushort*) ptr = *(ushort*)bufferPointer; - break; - case 3: - *(ushort*) ptr = *(ushort*)bufferPointer; - *(ptr+2) = *(bufferPointer+2); - break; - case 4: - *(uint*) ptr = *(uint*)bufferPointer; - break; - case 5: - *(uint*) ptr = *(uint*)bufferPointer; - *(ptr+4) = *(bufferPointer+4); - break; - case 6: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - break; - case 7: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - *(ptr+6) = *(bufferPointer+6); - break; - case 8: - *(ulong*) ptr = *(ulong*)bufferPointer; - break; - default: - UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); - break; - } + BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); m_Position += bytesToRead; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 508dc1d95e..d08dbdad17 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -11,14 +11,50 @@ public struct FastBufferWriter : IDisposable { internal unsafe byte* m_BufferPointer; internal int m_Position; - internal int m_Length; + private int m_Length; internal int m_Capacity; - internal int m_MaxCapacity; - internal Allocator m_Allocator; + internal readonly int m_MaxCapacity; + private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR internal int m_AllowedWriteMark; - internal bool m_InBitwiseContext; + private bool m_InBitwiseContext; #endif + + /// + /// The current write position + /// + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + /// + /// The current total buffer size + /// + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Capacity; + } + + /// + /// The maximum possible total buffer size + /// + public int MaxCapacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_MaxCapacity; + } + + /// + /// The total amount of bytes that have been written to the stream + /// + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position > m_Length ? m_Position : m_Length; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -30,6 +66,12 @@ internal void CommitBitwiseWrites(int amount) #endif } + /// + /// Create a FastBufferWriter. + /// + /// Size of the buffer to create + /// Allocator to use in creating it + /// Maximum size the buffer can grow to. If less than size, buffer cannot grow. public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) { void* buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); @@ -41,18 +83,26 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) m_Length = 0; m_Capacity = size; m_Allocator = allocator; - m_MaxCapacity = maxSize == -1 ? size : maxSize; + m_MaxCapacity = maxSize < size ? size : maxSize; #if DEVELOPMENT_BUILD || UNITY_EDITOR m_AllowedWriteMark = 0; m_InBitwiseContext = false; #endif } + /// + /// Frees the allocated buffer + /// public unsafe void Dispose() { UnsafeUtility.Free(m_BufferPointer, m_Allocator); } + /// + /// Move the write position in the stream. + /// Note that moving forward past the current length will extend the buffer's Length value even if you don't write. + /// + /// Absolute value to move the position to, truncated to Capacity [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { @@ -74,6 +124,11 @@ public void Seek(int where) m_Position = where; } + /// + /// Truncate the stream by setting Length to the specified value. + /// If Position is greater than the specified value, it will be moved as well. + /// + /// The value to truncate to. If -1, the current position will be used. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Truncate(int where = -1) { @@ -92,6 +147,12 @@ public void Truncate(int where = -1) } } + /// + /// Retrieve a BitWriter to be able to perform bitwise operations on the buffer. + /// No bytewise operations can be performed on the buffer until bitWriter.Dispose() has been called. + /// At the end of the operation, FastBufferWriter will remain byte-aligned. + /// + /// A BitWriter public BitWriter EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -99,25 +160,6 @@ public BitWriter EnterBitwiseContext() #endif return new BitWriter(ref this); } - - - public int Position - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; - } - - public int Capacity - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Capacity; - } - - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position > m_Length ? m_Position : m_Length; - } internal unsafe void Grow() { @@ -132,6 +174,20 @@ internal unsafe void Grow() m_Capacity = newSize; } + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// WriteValue() instead of WriteValueSafe() for faster serialization. + /// + /// Unsafe write operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following + /// operations in release builds. + /// + /// Amount of bytes to write + /// True if the write is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool VerifyCanWrite(int bytes) { @@ -158,9 +214,23 @@ public bool VerifyCanWrite(int bytes) #endif return true; } - + + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// WriteValue() instead of WriteValueSafe() for faster serialization. + /// + /// Unsafe write operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following + /// operations in release builds. + /// + /// The value you want to write + /// True if the write is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanWriteInternal(int bytes) + public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -169,7 +239,8 @@ public bool VerifyCanWriteInternal(int bytes) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Capacity) + int len = sizeof(T); + if (m_Position + len > m_Capacity) { if (m_Capacity < m_MaxCapacity) { @@ -181,16 +252,20 @@ public bool VerifyCanWriteInternal(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedWriteMark) - { - m_AllowedWriteMark = m_Position + bytes; - } + m_AllowedWriteMark = m_Position + len; #endif return true; } - + + /// + /// Internal version of VerifyCanWrite. + /// Differs from VerifyCanWrite only in that it won't ever move the AllowedWriteMark backward. + /// + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged + public bool VerifyCanWriteInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -199,8 +274,7 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - int len = sizeof(T); - if (m_Position + len > m_Capacity) + if (m_Position + bytes > m_Capacity) { if (m_Capacity < m_MaxCapacity) { @@ -212,11 +286,19 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = m_Position + len; + if (m_Position + bytes > m_AllowedWriteMark) + { + m_AllowedWriteMark = m_Position + bytes; + } #endif return true; } + /// + /// Returns an array representation of the underlying byte buffer. + /// !!Allocates a new array!! + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte[] ToArray() { @@ -228,12 +310,20 @@ public unsafe byte[] ToArray() return ret; } + /// + /// Gets a direct pointer to the underlying buffer + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { return m_BufferPointer; } + /// + /// Gets a direct pointer to the underlying buffer at the current read position + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { @@ -245,11 +335,15 @@ public unsafe byte[] ToArray() /// Named differently from other WriteValue methods to avoid accidental boxing /// /// The object to write + /// + /// If true, an extra byte will be written to indicate whether or not the value is null. + /// Some types will always write this. + /// public void WriteObject(object value, bool isNullable = false) { if (isNullable || value.GetType().IsNullable()) { - bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + bool isNull = value == null || (value is UnityEngine.Object o && o == null); WriteValueSafe(isNull); @@ -340,48 +434,102 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } + /// + /// Write an INetworkSerializable + /// + /// The value to write + /// public void WriteNetworkSerializable(in T value) where T : INetworkSerializable { // TODO } - + + /// + /// Get the required amount of space to write a GameObject + /// + /// + /// + public static int GetWriteSize(GameObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required amount of space to write a GameObject + /// + /// + public static int GetGameObjectWriteSize() + { + return sizeof(ulong); + } + + /// + /// Write a GameObject + /// + /// The value to write public void WriteValue(GameObject value) { - var networkObject = ((GameObject)value).GetComponent(); + var networkObject = (value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); } WriteValue(networkObject.NetworkObjectId); } + /// + /// Write a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. + /// + /// The value to write public void WriteValueSafe(GameObject value) { - var networkObject = ((GameObject)value).GetComponent(); + var networkObject = (value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); } WriteValueSafe(networkObject.NetworkObjectId); } - public static int GetWriteSize(GameObject value) + /// + /// Get the required size to write a NetworkObject + /// + /// + /// + public static int GetWriteSize(NetworkObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required size to write a NetworkObject + /// + /// + public static int GetNetworkObjectWriteSize() { return sizeof(ulong); } + + /// + /// Write a NetworkObject + /// + /// The value to write public void WriteValue(in NetworkObject value) { if (!value.IsSpawned) @@ -392,6 +540,13 @@ public void WriteValue(in NetworkObject value) WriteValue(value.NetworkObjectId); } + /// + /// Write a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. + /// + /// The value to write public void WriteValueSafe(NetworkObject value) { if (!value.IsSpawned) @@ -401,27 +556,56 @@ public void WriteValueSafe(NetworkObject value) WriteValueSafe(value.NetworkObjectId); } - public static int GetWriteSize(NetworkObject value) + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + /// + public static int GetWriteSize(NetworkBehaviour value) { - return sizeof(ulong); + return sizeof(ulong) + sizeof(ushort); + } + + + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + public static int GetNetworkBehaviourWriteSize() + { + return sizeof(ulong) + sizeof(ushort); } + + /// + /// Write a NetworkBehaviour + /// + /// The value to write public void WriteValue(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); } WriteValue(value.NetworkObjectId); WriteValue(value.NetworkBehaviourId); } + /// + /// Write a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. + /// + /// The value to write + /// + /// public void WriteValueSafe(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); } if (!VerifyCanWriteInternal(sizeof(ulong) + sizeof(ushort))) @@ -431,10 +615,17 @@ public void WriteValueSafe(NetworkBehaviour value) WriteValue(value.NetworkObjectId); WriteValue(value.NetworkBehaviourId); } - - public static int GetWriteSize(NetworkBehaviour value) + + /// + /// Get the required size to write a string + /// + /// The string to write + /// Whether or not to use one byte per character. This will only allow ASCII + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetWriteSize(string s, bool oneByteChars = false) { - return sizeof(ulong) + sizeof(ushort); + return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); } /// @@ -464,6 +655,9 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) /// /// Writes a string + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// The string to write /// Whether or not to use one byte per character. This will only allow ASCII @@ -502,10 +696,20 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) } } + /// + /// Get the required size to write an unmanaged array + /// + /// The array to write + /// The amount of elements to write + /// Where in the array to start + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetWriteSize(string s, bool oneByteChars = false) + public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged { - return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + return sizeof(int) + sizeInBytes; } /// @@ -529,6 +733,9 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher /// /// Writes an unmanaged array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// The array to write /// The amount of elements to write @@ -558,22 +765,19 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) WriteBytes(bytes, sizeInBytes); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged - { - int sizeInTs = count != -1 ? count : array.Length - offset; - int sizeInBytes = sizeInTs * sizeof(T); - return sizeof(int) + sizeInBytes; - } - + + /// + /// Write a partial value. The specified number of bytes is written from the value and the rest is ignored. + /// + /// Value to write + /// Number of bytes + /// Offset into the value to begin reading the bytes + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -588,41 +792,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToWrite) - { - case 1: - bufferPointer[0] = *ptr; - break; - case 2: - *(ushort*) bufferPointer = *(ushort*)ptr; - break; - case 3: - *(ushort*) bufferPointer = *(ushort*)ptr; - *(bufferPointer+2) = *(ptr+2); - break; - case 4: - *(uint*) bufferPointer = *(uint*)ptr; - break; - case 5: - *(uint*) bufferPointer = *(uint*)ptr; - *(bufferPointer+4) = *(ptr+4); - break; - case 6: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - break; - case 7: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - *(bufferPointer+6) = *(ptr+6); - break; - case 8: - *(ulong*) bufferPointer = *(ulong*)ptr; - break; - default: - UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); - break; - } + BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); m_Position += bytesToWrite; } @@ -650,6 +820,9 @@ public unsafe void WriteByte(byte value) /// /// Write a byte to the stream. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -669,12 +842,13 @@ public unsafe void WriteByteSafe(byte value) } m_BufferPointer[m_Position++] = value; } - + /// /// Write multiple bytes to the stream /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte* value, int size, int offset = 0) { @@ -695,9 +869,13 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) /// /// Write multiple bytes to the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { @@ -722,6 +900,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte[] value, int size, int offset = 0) { @@ -733,9 +912,13 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) /// /// Write multiple bytes to the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) { @@ -768,9 +951,32 @@ public unsafe void CopyFrom(FastBufferWriter other) { WriteBytes(other.m_BufferPointer, other.m_Position); } + + /// + /// Get the size required to write an unmanaged value + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(in T value) where T : unmanaged + { + return sizeof(T); + } + + /// + /// Get the size required to write an unmanaged value of type T + /// + /// + /// + /// + public static unsafe int GetWriteSize() where T : unmanaged + { + return sizeof(T); + } /// - /// Write a value of any unmanaged type to the buffer. + /// Write a value of any unmanaged type (including unmanaged structs) to the buffer. /// It will be copied into the buffer exactly as it exists in memory. /// /// The value to copy @@ -798,8 +1004,11 @@ public unsafe void WriteValue(in T value) where T : unmanaged } /// - /// Write a value of any unmanaged type to the buffer. + /// Write a value of any unmanaged type (including unmanaged structs) to the buffer. /// It will be copied into the buffer exactly as it exists in memory. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// The value to copy /// Any unmanaged type @@ -825,31 +1034,5 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged *pointer = value; m_Position += len; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(in T value) where T : unmanaged - { - return sizeof(T); - } - - public static unsafe int GetWriteSize() where T : unmanaged - { - return sizeof(T); - } - - public static int GetNetworkObjectWriteSize() - { - return sizeof(ulong); - } - - public static int GetGameObjectWriteSize() - { - return sizeof(ulong); - } - - public static int GetNetworkBehaviourWriteSize() - { - return sizeof(ulong) + sizeof(ushort); - } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs rename to com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta new file mode 100644 index 0000000000..f5c741d324 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9220f80eab4051e4d9b9504ef840eba4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index 2d714c2642..28cc2ad1bb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -4,6 +4,17 @@ namespace Unity.Multiplayer.Netcode { + /// + /// Registry for telling FastBufferWriter and FastBufferReader how to read types when passed to + /// WriteObject and ReadObject, as well as telling BytePacker and ByteUnpacker how to do it when passed to + /// WriteObjectPacked and ReadObjectPacked. + /// + /// These object-based serialization functions shouldn't be used if at all possible, but if they're required, + /// and you need to serialize a type that's not natively supported, you can register it with the dictionaries here: + /// + /// Serializers and Deserializers for FastBufferWriter and FasteBufferReader + /// SerializersPacked and DeserializersPacked for BytePacker and ByteUnpacker + /// public static class SerializationTypeTable { public delegate void Serialize(ref FastBufferWriter writer, object value); From 63309e2c1b6a483c3740ac078cc1b8835a575d3d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 13:42:39 -0500 Subject: [PATCH 10/58] Replaced possibly unaligned memory access with UnsafeUtility.MemCpy... it's a little slower, but apparently some platforms won't support unaligned memory access (e.g., WEBGL, ARM processors) and there's no compile time way to detect ARM processors since the bytecode is not processor-dependent... the cost of the runtime detection would be more expensive than the cost of just doing the memcpy. --- .../Runtime/Serialization/BitReader.cs | 3 +- .../Runtime/Serialization/BitWriter.cs | 3 +- .../Runtime/Serialization/BytewiseUtility.cs | 57 ------------------- .../Serialization/BytewiseUtility.cs.meta | 11 ---- .../Runtime/Serialization/FastBufferReader.cs | 15 +++-- .../Runtime/Serialization/FastBufferWriter.cs | 10 ++-- 6 files changed, 20 insertions(+), 79 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 7c564c6d0a..c784922a34 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Multiplayer.Netcode @@ -175,7 +176,7 @@ private unsafe void ReadPartialValue(out T value, int bytesToRead, int offset T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 5bbf095641..e103dce473 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { @@ -186,7 +187,7 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy { byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * BITS_PER_BYTE; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs deleted file mode 100644 index de1e617069..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Multiplayer.Netcode -{ - public static class BytewiseUtility - { - /// - /// Helper function optimized for quickly copying small numbers of bytes. - /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 - /// Slower for amount > 8 - /// - /// Pointer to the source value - /// Pointer to the destination value - /// Number of bytes to copy - public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) - { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - switch (amount) - { - case 1: - dest[0] = *source; - break; - case 2: - *(ushort*) dest = *(ushort*)source; - break; - case 3: - *(ushort*) dest = *(ushort*)source; - *(dest+2) = *(source+2); - break; - case 4: - *(uint*) dest = *(uint*)source; - break; - case 5: - *(uint*) dest = *(uint*)source; - *(dest+4) = *(source+4); - break; - case 6: - *(uint*) dest = *(uint*)source; - *(ushort*) (dest+4) = *(ushort*)(source+4); - break; - case 7: - *(uint*) dest = *(uint*)source; - *(ushort*) (dest+4) = *(ushort*)(source+4); - *(dest+6) = *(source+6); - break; - case 8: - *(ulong*) dest = *(ulong*)source; - break; - default: - UnsafeUtility.MemCpy(dest, source, amount); - break; - } - } - } -} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta deleted file mode 100644 index 12bebf3d01..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5df078ced492f8c45966997ceda09c8f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index ac571ad9c3..4a25be7d29 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -782,7 +782,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); m_Position += bytesToRead; value = val; @@ -942,8 +942,10 @@ public unsafe void ReadValue(out T value) where T : unmanaged } #endif - T* pointer = (T*)(m_BufferPointer+m_Position); - value = *pointer; + fixed (T* ptr = &value) + { + UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + } m_Position += len; } @@ -974,8 +976,11 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged throw new OverflowException("Writing past the end of the buffer"); } - T* pointer = (T*)(m_BufferPointer+m_Position); - value = *pointer; + + fixed (T* ptr = &value) + { + UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + } m_Position += len; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index d08dbdad17..84e2abfc6b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -792,7 +792,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); m_Position += bytesToWrite; } @@ -1029,9 +1029,11 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged { throw new OverflowException("Writing past the end of the buffer"); } - - T* pointer = (T*)(m_BufferPointer+m_Position); - *pointer = value; + + fixed (T* ptr = &value) + { + UnsafeUtility.MemCpy(m_BufferPointer+m_Position, ptr, len); + } m_Position += len; } } From e9b93056aab3b57c18e694d263a45976b5b26b4c Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:00:18 -0500 Subject: [PATCH 11/58] Resurrected BytewiseUtil.FastCopyBytes as a faster alternative to UnsafeUtility.MemCpy for small values, while still supporting unaligned access. --- .../Runtime/Serialization/BitReader.cs | 2 +- .../Runtime/Serialization/BitWriter.cs | 2 +- .../Runtime/Serialization/ByteUnpacker.cs | 55 ++++++++++--- .../Runtime/Serialization/BytewiseUtility.cs | 80 +++++++++++++++++++ .../Serialization/BytewiseUtility.cs.meta | 11 +++ .../Runtime/Serialization/FastBufferReader.cs | 4 +- .../Runtime/Serialization/FastBufferWriter.cs | 8 +- .../Editor/Serialization/BitCounterTests.cs | 5 +- 8 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index c784922a34..60f84c8b74 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -176,7 +176,7 @@ private unsafe void ReadPartialValue(out T value, int bytesToRead, int offset T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); + BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index e103dce473..db70356262 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -187,7 +187,7 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy { byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); + BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * BITS_PER_BYTE; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index dc827b40b6..a5601dfd60 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -477,7 +477,8 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us *ptr = *data; break; case 2: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); break; default: throw new InvalidOperationException("Could not read bit-packed value: impossible byte count"); @@ -521,14 +522,19 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui *ptr = *data; break; case 2: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); break; case 3: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); *(ptr+2) = *(data+2); break; case 4: - *(uint*) ptr = *(uint*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); break; } @@ -570,30 +576,53 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul *ptr = *data; break; case 2: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); break; case 3: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); *(ptr+2) = *(data+2); break; case 4: - *(uint*) ptr = *(uint*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); break; case 5: - *(uint*) ptr = *(uint*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); *(ptr+4) = *(data+4); break; case 6: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); + *(ptr+4) = *(data+4); + *(ptr+5) = *(data+5); break; case 7: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); + *(ptr+4) = *(data+4); + *(ptr+5) = *(data+5); *(ptr+6) = *(data+6); break; case 8: - *(ulong*) ptr = *(ulong*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); + *(ptr+4) = *(data+4); + *(ptr+5) = *(data+5); + *(ptr+6) = *(data+6); + *(ptr+7) = *(data+7); break; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs new file mode 100644 index 0000000000..1cfda38e3c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -0,0 +1,80 @@ +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Multiplayer.Netcode +{ + public static class BytewiseUtility + { + /// + /// Helper function optimized for quickly copying small numbers of bytes. + /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 + /// Slower for amount > 8 + /// + /// Pointer to the source value + /// Pointer to the destination value + /// Number of bytes to copy + public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + switch (amount) + { + case 1: + *dest = *source; + break; + case 2: + *dest = *source; + *(dest + 1) = *(source + 1); + break; + case 3: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + break; + case 4: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + break; + case 5: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + break; + case 6: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + *(dest + 5) = *(source + 5); + break; + case 7: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + *(dest + 5) = *(source + 5); + *(dest + 6) = *(source + 6); + break; + case 8: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + *(dest + 5) = *(source + 5); + *(dest + 6) = *(source + 6); + *(dest + 7) = *(source + 7); + break; + default: + UnsafeUtility.MemCpy(dest, source, amount); + break; + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta new file mode 100644 index 0000000000..12bebf3d01 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5df078ced492f8c45966997ceda09c8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 4a25be7d29..6501441f71 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -944,7 +944,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged fixed (T* ptr = &value) { - UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); } m_Position += len; } @@ -979,7 +979,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged fixed (T* ptr = &value) { - UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); } m_Position += len; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 84e2abfc6b..55e1ba0b6b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -998,8 +998,10 @@ public unsafe void WriteValue(in T value) where T : unmanaged } #endif - T* pointer = (T*)(m_BufferPointer+m_Position); - *pointer = value; + fixed (T* ptr = &value) + { + BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); + } m_Position += len; } @@ -1032,7 +1034,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged fixed (T* ptr = &value) { - UnsafeUtility.MemCpy(m_BufferPointer+m_Position, ptr, len); + BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); } m_Position += len; } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 12331d6ff5..b3d216b7ca 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,5 +1,8 @@ -using NUnit.Framework; +using System.Diagnostics; +using NUnit.Framework; +using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; +using Debug = UnityEngine.Debug; namespace Unity.Netcode.EditorTests { From aa56440dfc19059c4bc55bdc65f452ecd283aa0f Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:19:44 -0500 Subject: [PATCH 12/58] Reverting an accidental change. --- .../Tests/Editor/Serialization/BitCounterTests.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index b3d216b7ca..12331d6ff5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,8 +1,5 @@ -using System.Diagnostics; -using NUnit.Framework; -using Unity.Collections.LowLevel.Unsafe; +using NUnit.Framework; using Unity.Multiplayer.Netcode; -using Debug = UnityEngine.Debug; namespace Unity.Netcode.EditorTests { From fabb3b8b65734dc4dc83fbaed92abd5be21082ff Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:35:19 -0500 Subject: [PATCH 13/58] Removed files that got accidentally duplicated from before the rename. --- .../Runtime/Serialization.meta | 8 - .../Runtime/Serialization/FastBufferWriter.cs | 254 ------------------ .../Serialization/FastBufferWriter.cs.meta | 11 - 3 files changed, 273 deletions(-) delete mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization.meta delete mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs delete mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization.meta deleted file mode 100644 index 7ec99bd274..0000000000 --- a/com.unity.multiplayer.mlapi/Runtime/Serialization.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4fd73dafe8658fb4cb3c87f77e783073 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs deleted file mode 100644 index ddf56fcd51..0000000000 --- a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System.Runtime.CompilerServices; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode; - -namespace Unity.Multiplayer.Netcode -{ - public struct FastBufferWriter - { - private NativeArray m_buffer; - private readonly unsafe byte* m_bufferPointer; - private int m_position; - - public unsafe FastBufferWriter(NativeArray buffer, int position = 0) - { - m_buffer = buffer; - m_bufferPointer = (byte*) m_buffer.GetUnsafePtr(); - m_position = position; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Seek(int where) - { - m_position = where; - } - - public int Position => m_position; - - public NativeArray GetNativeArray() - { - return m_buffer; - } - - public byte[] ToArray() - { - return m_buffer.ToArray(); - } - - public unsafe byte* GetUnsafePtr() - { - return m_bufferPointer; - } - - public unsafe byte* GetUnsafePtrAtCurrentPosition() - { - return m_bufferPointer + m_position; - } - - /// - /// Write single-precision floating point value to the stream as a varint - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSinglePacked(float value) - { - WriteUInt32Packed(ToUint(value)); - } - - /// - /// Write double-precision floating point value to the stream as a varint - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteDoublePacked(double value) - { - WriteUInt64Packed(ToUlong(value)); - } - - /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt16Packed(short value) => WriteInt64Packed(value); - - /// - /// Write an unsigned short (UInt16) as a varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt16Packed(ushort value) => WriteUInt64Packed(value); - - /// - /// Write a two-byte character as a varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteCharPacked(char c) => WriteUInt16Packed(c); - - /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt32Packed(int value) => WriteInt64Packed(value); - - /// - /// Write an unsigned int (UInt32) as a varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt32Packed(uint value) => WriteUInt64Packed(value); - - /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt64Packed(long value) => WriteUInt64Packed(Arithmetic.ZigZagEncode(value)); - - /// - /// Write an unsigned long (UInt64) as a varint to the stream. - /// - /// Value to write - public void WriteUInt64Packed(ulong value) - { - if (value <= 240) - { - WriteULongByte(value); - } - else if (value <= 2287) - { - WriteULongByte(((value - 240) >> 8) + 241); - WriteULongByte(value - 240); - } - else if (value <= 67823) - { - WriteByte(249); - WriteULongByte((value - 2288) >> 8); - WriteULongByte(value - 2288); - } - else - { - ulong header = 255; - ulong match = 0x00FF_FFFF_FFFF_FFFFUL; - while (value <= match) - { - --header; - match >>= 8; - } - - WriteULongByte(header); - int max = (int)(header - 247); - for (int i = 0; i < max; ++i) - { - WriteULongByte(value >> (i << 3)); - } - } - } - - /// - /// Write a byte (in an int format) to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteIntByte(int value) => WriteByte((byte)value); - - /// - /// Write a byte (in a ulong format) to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteULongByte(ulong byteValue) => WriteByte((byte)byteValue); - - /// - /// Write a byte to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteByte(byte value) - { - m_bufferPointer[m_position++] = value; - } - - /// - /// Write multiple bytes to the stream - /// - /// Value to write - /// Number of bytes to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteBytes(byte* value, int size) - { - UnsafeUtility.MemCpy((m_bufferPointer + m_position), value, size); - m_position += size; - } - - /// - /// Copy the contents of this writer into another writer. - /// The contents will be copied from the beginning of this writer to its current position. - /// They will be copied to the other writer starting at the other writer's current position. - /// - /// Writer to copy to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(FastBufferWriter other) - { - other.WriteBytes(m_bufferPointer, m_position); - } - - /// - /// Copy the contents of another writer into this writer. - /// The contents will be copied from the beginning of the other writer to its current position. - /// They will be copied to this writer starting at this writer's current position. - /// - /// Writer to copy to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyFrom(FastBufferWriter other) - { - WriteBytes(other.m_bufferPointer, other.m_position); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe uint ToUint(T value) where T : unmanaged - { - uint* asUint = (uint*) &value; - return *asUint; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe ulong ToUlong(T value) where T : unmanaged - { - ulong* asUlong = (ulong*) &value; - return *asUlong; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe float ToSingle(T value) where T : unmanaged - { - float* asFloat = (float*) &value; - return *asFloat; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe double ToDouble(T value) where T : unmanaged - { - double* asDouble = (double*) &value; - return *asDouble; - } - - /// - /// Write a value of any unmanaged type to the buffer. - /// It will be copied into the buffer exactly as it exists in memory. - /// - /// The value to copy - /// Any unmanaged type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValue(in T value) where T : unmanaged - { - int len = sizeof(T); - T* pointer = (T*)(m_bufferPointer+m_position); - *pointer = value; - m_position += len; - } - } -} \ No newline at end of file diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta deleted file mode 100644 index 6a6eb1001b..0000000000 --- a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b6cac99a38cd00a41a020d7f46dfb0f4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 3a2a50b837d7bfd7faa502f87ccca8921e126e63 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:57:19 -0500 Subject: [PATCH 14/58] Standards fixes --- .../Runtime/Serialization/BitCounter.cs | 34 +-- .../Runtime/Serialization/BitReader.cs | 40 +-- .../Runtime/Serialization/BitWriter.cs | 49 ++-- .../Runtime/Serialization/BufferSerializer.cs | 38 +-- .../Serialization/BufferSerializerReader.cs | 10 +- .../Serialization/BufferSerializerWriter.cs | 8 +- .../Runtime/Serialization/BytePacker.cs | 46 ++-- .../Runtime/Serialization/ByteUnpacker.cs | 112 ++++----- .../Runtime/Serialization/BytewiseUtility.cs | 4 +- .../Runtime/Serialization/FastBufferReader.cs | 234 +++++++++--------- .../Runtime/Serialization/FastBufferWriter.cs | 214 ++++++++-------- .../IBufferSerializerImplementation.cs | 9 +- .../Serialization/SerializationTypeTable.cs | 172 ++++++------- .../Runtime/Utility/Ref.cs | 6 +- .../Runtime/Utility/RefArray.cs | 38 +-- .../Editor/Serialization/BitCounterTests.cs | 16 +- .../Editor/Serialization/BitReaderTests.cs | 68 ++--- .../Editor/Serialization/BitWriterTests.cs | 118 ++++----- .../Serialization/BufferSerializerTests.cs | 212 ++++++++-------- .../Editor/Serialization/BytePackerTests.cs | 187 +++++++------- .../Serialization/FastBufferReaderTests.cs | 173 +++++++------ .../Serialization/FastBufferWriterTests.cs | 233 +++++++++-------- .../Tests/Runtime/NetworkSceneManagerTests.cs | 12 +- 23 files changed, 1006 insertions(+), 1027 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs index 44c09dc8cf..fad7dcf093 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace Unity.Multiplayer.Netcode { @@ -11,7 +11,7 @@ public static class BitCounter private const uint k_DeBruijnMagic32 = 0x06EB14F9; // We're counting bytes, not bits, so these have all had the operation x/8 + 1 applied - private static readonly int[] k_deBruijnTableBytes64 = + private static readonly int[] k_DeBruijnTableBytes64 = { 0/8+1, 1/8+1, 17/8+1, 2/8+1, 18/8+1, 50/8+1, 3/8+1, 57/8+1, 47/8+1, 19/8+1, 22/8+1, 51/8+1, 29/8+1, 4/8+1, 33/8+1, 58/8+1, @@ -22,18 +22,18 @@ public static class BitCounter 62/8+1, 55/8+1, 45/8+1, 31/8+1, 13/8+1, 39/8+1, 36/8+1, 6/8+1, 61/8+1, 44/8+1, 12/8+1, 35/8+1, 60/8+1, 11/8+1, 10/8+1, 9/8+1, }; - - private static readonly int[] k_deBruijnTableBytes32 = + + private static readonly int[] k_DeBruijnTableBytes32 = { - 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, + 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, 30/8+1, 20/8+1, 18/8+1, 11/8+1, 13/8+1, 4/8+1, 7/8+1, 23/8+1, - 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, + 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, 14/8+1, 27/8+1, 9/8+1, 5/8+1, 26/8+1, 8/8+1, 25/8+1, 24/8+1, }; - + // And here we're counting the number of set bits, not the position of the highest set, // so these still have +1 applied - unfortunately 0 and 1 both return the same value. - private static readonly int[] k_deBruijnTableBits64 = + private static readonly int[] k_DeBruijnTableBits64 = { 0+1, 1+1, 17+1, 2+1, 18+1, 50+1, 3+1, 57+1, 47+1, 19+1, 22+1, 51+1, 29+1, 4+1, 33+1, 58+1, @@ -44,12 +44,12 @@ public static class BitCounter 62+1, 55+1, 45+1, 31+1, 13+1, 39+1, 36+1, 6+1, 61+1, 44+1, 12+1, 35+1, 60+1, 11+1, 10+1, 9+1, }; - - private static readonly int[] k_deBruijnTableBits32 = + + private static readonly int[] k_DeBruijnTableBits32 = { - 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, + 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, 30+1, 20+1, 18+1, 11+1, 13+1, 4+1, 7+1, 23+1, - 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, + 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, }; @@ -67,7 +67,7 @@ public static int GetUsedByteCount(uint value) value |= value >> 8; value |= value >> 16; value = value & ~(value >> 1); - return k_deBruijnTableBytes32[value*k_DeBruijnMagic32 >> 27]; + return k_DeBruijnTableBytes32[value * k_DeBruijnMagic32 >> 27]; } /// @@ -85,7 +85,7 @@ public static int GetUsedByteCount(ulong value) value |= value >> 16; value |= value >> 32; value = value & ~(value >> 1); - return k_deBruijnTableBytes64[value*k_DeBruijnMagic64 >> 58]; + return k_DeBruijnTableBytes64[value * k_DeBruijnMagic64 >> 58]; } /// @@ -102,7 +102,7 @@ public static int GetUsedBitCount(uint value) value |= value >> 8; value |= value >> 16; value = value & ~(value >> 1); - return k_deBruijnTableBits32[value*k_DeBruijnMagic32 >> 27]; + return k_DeBruijnTableBits32[value * k_DeBruijnMagic32 >> 27]; } /// @@ -120,7 +120,7 @@ public static int GetUsedBitCount(ulong value) value |= value >> 16; value |= value >> 32; value = value & ~(value >> 1); - return k_deBruijnTableBits64[value*k_DeBruijnMagic64 >> 58]; + return k_DeBruijnTableBits64[value * k_DeBruijnMagic64 >> 58]; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 60f84c8b74..01ddbc6b91 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Multiplayer.Netcode @@ -15,12 +14,13 @@ public ref struct BitReader private Ref m_Reader; private readonly unsafe byte* m_BufferPointer; private readonly int m_Position; - private const int BITS_PER_BYTE = 8; private int m_BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR private int m_AllowedBitwiseReadMark; #endif + private const int k_BitsPerByte = 8; + /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. /// @@ -34,11 +34,11 @@ internal unsafe BitReader(ref FastBufferReader reader) { m_Reader = new Ref(ref reader); - m_BufferPointer = m_Reader.Value.m_BufferPointer + m_Reader.Value.Position; + m_BufferPointer = m_Reader.Value.BufferPointer + m_Reader.Value.Position; m_Position = m_Reader.Value.Position; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseReadMark = (m_Reader.Value.m_AllowedReadMark - m_Position) * BITS_PER_BYTE; + m_AllowedBitwiseReadMark = (m_Reader.Value.AllowedReadMark - m_Position) * k_BitsPerByte; #endif } @@ -75,7 +75,7 @@ public bool VerifyCanReadBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Reader.Value.m_Position + totalBytesWrittenInBitwiseContext > m_Reader.Value.m_Length) + if (m_Reader.Value.PositionInternal + totalBytesWrittenInBitwiseContext > m_Reader.Value.LengthInternal) { return false; } @@ -102,7 +102,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) { throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); } - + int checkPos = (m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { @@ -111,8 +111,8 @@ public unsafe void ReadBits(out ulong value, int bitCount) #endif ulong val = 0; - int wholeBytes = bitCount / BITS_PER_BYTE; - byte* asBytes = (byte*) &val; + int wholeBytes = bitCount / k_BitsPerByte; + byte* asBytes = (byte*)&val; if (BitAligned) { if (wholeBytes != 0) @@ -127,11 +127,11 @@ public unsafe void ReadBits(out ulong value, int bitCount) ReadMisaligned(out asBytes[i]); } } - + val |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); value = val; } - + /// /// Read bits from stream. /// @@ -163,25 +163,25 @@ public unsafe void ReadBit(out bool bit) throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } #endif - + int offset = m_BitPosition & 7; int pos = m_BitPosition >> 3; bit = (m_BufferPointer[pos] & (1 << offset)) != 0; ++m_BitPosition; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + private unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T : unmanaged { - T val = new T(); - byte* ptr = ((byte*) &val) + offsetBytes; + var val = new T(); + byte* ptr = ((byte*)&val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); - m_BitPosition += bytesToRead * BITS_PER_BYTE; + m_BitPosition += bytesToRead * k_BitsPerByte; value = val; } - + private byte ReadByteBits(int bitCount) { if (bitCount > 8) @@ -211,8 +211,8 @@ private unsafe void ReadMisaligned(out byte value) int off = m_BitPosition & 7; int pos = m_BitPosition >> 3; int shift1 = 8 - off; - + value = (byte)((m_BufferPointer[pos] >> shift1) | (m_BufferPointer[(m_BitPosition += 8) >> 3] << shift1)); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index db70356262..903b279f76 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { @@ -18,7 +17,7 @@ public ref struct BitWriter #if DEVELOPMENT_BUILD || UNITY_EDITOR private int m_AllowedBitwiseWriteMark; #endif - private const int BITS_PER_BYTE = 8; + private const int k_BitsPerByte = 8; /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. @@ -28,15 +27,15 @@ public bool BitAligned [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (m_BitPosition & 7) == 0; } - + internal unsafe BitWriter(ref FastBufferWriter writer) { m_Writer = new Ref(ref writer); - m_BufferPointer = writer.m_BufferPointer + writer.m_Position; - m_Position = writer.m_Position; + m_BufferPointer = writer.BufferPointer + writer.PositionInternal; + m_Position = writer.PositionInternal; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseWriteMark = (m_Writer.Value.m_AllowedWriteMark - m_Writer.Value.Position) * BITS_PER_BYTE; + m_AllowedBitwiseWriteMark = (m_Writer.Value.AllowedWriteMark - m_Writer.Value.Position) * k_BitsPerByte; #endif } @@ -54,7 +53,7 @@ public void Dispose() m_Writer.Value.CommitBitwiseWrites(bytesWritten); } - + /// /// Verifies the requested bit count can be written to the buffer. /// This exists as a separate method to allow multiple bit writes to be bounds checked with a single call. @@ -74,12 +73,12 @@ public unsafe bool VerifyCanWriteBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.m_Capacity) + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.CapacityInternal) { - if (m_Writer.Value.m_Capacity < m_Writer.Value.m_MaxCapacity) + if (m_Writer.Value.CapacityInternal < m_Writer.Value.MaxCapacityInternal) { m_Writer.Value.Grow(); - m_BufferPointer = m_Writer.Value.m_BufferPointer + m_Writer.Value.m_Position; + m_BufferPointer = m_Writer.Value.BufferPointer + m_Writer.Value.PositionInternal; } else { @@ -109,7 +108,7 @@ public unsafe void WriteBits(ulong value, int bitCount) { throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); } - + int checkPos = (m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { @@ -117,8 +116,8 @@ public unsafe void WriteBits(ulong value, int bitCount) } #endif - int wholeBytes = bitCount / BITS_PER_BYTE; - byte* asBytes = (byte*) &value; + int wholeBytes = bitCount / k_BitsPerByte; + byte* asBytes = (byte*)&value; if (BitAligned) { if (wholeBytes != 0) @@ -133,13 +132,13 @@ public unsafe void WriteBits(ulong value, int bitCount) WriteMisaligned(asBytes[i]); } } - - for (var count = wholeBytes * BITS_PER_BYTE; count < bitCount; ++count) + + for (var count = wholeBytes * k_BitsPerByte; count < bitCount; ++count) { WriteBit((value & (1UL << count)) != 0); } } - + /// /// Write bits to stream. /// @@ -154,7 +153,7 @@ public void WriteBits(byte value, int bitCount) throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - + for (int i = 0; i < bitCount; ++i) { WriteBit(((value >> i) & 1) != 0); @@ -175,23 +174,23 @@ public unsafe void WriteBit(bool bit) throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - + int offset = m_BitPosition & 7; int pos = m_BitPosition >> 3; ++m_BitPosition; m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged { - byte* ptr = ((byte*) &value) + offsetBytes; + byte* ptr = ((byte*)&value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); - m_BitPosition += bytesToWrite * BITS_PER_BYTE; + m_BitPosition += bytesToWrite * k_BitsPerByte; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WriteMisaligned(byte value) { @@ -204,4 +203,4 @@ private unsafe void WriteMisaligned(byte value) m_BitPosition += 8; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index cfe11aed19..bba0c112c9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -26,12 +26,12 @@ namespace Unity.Netcode.Serialization public ref struct BufferSerializer where TImplementation : IBufferSerializerImplementation { private TImplementation m_Implementation; - + /// /// Check if the contained implementation is a reader /// public bool IsReader => m_Implementation.IsReader; - + /// /// Check if the contained implementation is a writer /// @@ -51,7 +51,7 @@ public ref FastBufferReader GetFastBufferReader() { return ref m_Implementation.GetFastBufferReader(); } - + /// /// Retrieves the FastBufferWriter instance. Only valid if IsWriter = true, throws /// InvalidOperationException otherwise. @@ -82,7 +82,7 @@ public void SerializeValue(ref object value, Type type, bool isNullable = false) { m_Implementation.SerializeValue(ref value, type, isNullable); } - + /// /// Serialize an INetworkSerializable /// If your INetworkSerializable is implemented by a struct, as opposed to a class, use this @@ -101,7 +101,7 @@ public void SerializeNetworkSerializable(ref T value) where T : INetworkSeria { m_Implementation.SerializeNetworkSerializable(ref value); } - + /// /// Serialize an INetworkSerializable /// @@ -113,7 +113,7 @@ public void SerializeValue(ref INetworkSerializable value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize a GameObject /// @@ -125,7 +125,7 @@ public void SerializeValue(ref GameObject value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize a NetworkObject /// @@ -137,7 +137,7 @@ public void SerializeValue(ref NetworkObject value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize a NetworkBehaviour /// @@ -167,7 +167,7 @@ public void SerializeValue(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValue(ref s, oneByteChars); } - + /// /// Serialize an array value. /// @@ -187,7 +187,7 @@ public void SerializeValue(ref T[] array) where T : unmanaged { m_Implementation.SerializeValue(ref array); } - + /// /// Serialize a single byte /// @@ -199,7 +199,7 @@ public void SerializeValue(ref byte value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. @@ -240,7 +240,7 @@ public void SerializeValuePreChecked(ref GameObject value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize a NetworkObject /// @@ -249,7 +249,7 @@ public void SerializeValuePreChecked(ref NetworkObject value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize a NetworkBehaviour /// @@ -258,7 +258,7 @@ public void SerializeValuePreChecked(ref NetworkBehaviour value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize a string. /// @@ -273,7 +273,7 @@ public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); } - + /// /// Serialize an array value. /// @@ -290,7 +290,7 @@ public void SerializeValuePreChecked(ref T[] array) where T : unmanaged { m_Implementation.SerializeValuePreChecked(ref array); } - + /// /// Serialize a single byte /// @@ -299,7 +299,7 @@ public void SerializeValuePreChecked(ref byte value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. @@ -310,4 +310,4 @@ public void SerializeValuePreChecked(ref T value) where T : unmanaged m_Implementation.SerializeValuePreChecked(ref value); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index e809cec933..146adc6cff 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -7,12 +7,12 @@ namespace Unity.Netcode.Serialization internal struct BufferSerializerReader : IBufferSerializerImplementation { private Ref m_Reader; - + public BufferSerializerReader(ref FastBufferReader reader) { m_Reader = new Ref(ref reader); } - + public bool IsReader => true; public bool IsWriter => false; @@ -20,7 +20,7 @@ public ref FastBufferReader GetFastBufferReader() { return ref m_Reader.Value; } - + public ref FastBufferWriter GetFastBufferWriter() { throw new InvalidOperationException("Cannot retrieve a FastBufferWriter from a serializer where IsWriter = false"); @@ -116,4 +116,4 @@ public void SerializeValuePreChecked(ref T value) where T : unmanaged m_Reader.Value.ReadValue(out value); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 916c0f51ef..8349ea0d3c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -12,10 +12,10 @@ public BufferSerializerWriter(ref FastBufferWriter writer) { m_Writer = new Ref(ref writer); } - + public bool IsReader => false; public bool IsWriter => true; - + public ref FastBufferReader GetFastBufferReader() { throw new InvalidOperationException("Cannot retrieve a FastBufferReader from a serializer where IsReader = false"); @@ -116,4 +116,4 @@ public void SerializeValuePreChecked(ref T value) where T : unmanaged m_Writer.Value.WriteValue(value); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index f935ad400d..c0e8672179 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using Unity.Netcode; using UnityEngine; @@ -40,7 +40,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, return; } } - + var type = value.GetType(); var hasSerializer = SerializationTypeTable.SerializersPacked.TryGetValue(type, out var serializer); if (hasSerializer) @@ -48,7 +48,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, serializer(ref writer, value); return; } - + if (value is Array array) { WriteValuePacked(ref writer, array.Length); @@ -58,7 +58,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, WriteObjectPacked(ref writer, array.GetValue(i)); } } - + if (value.GetType().IsEnum) { switch (Convert.GetTypeCode(value)) @@ -142,9 +142,9 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } #endregion - + #region Unmanaged Type Packing - + #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -176,7 +176,7 @@ public static unsafe void WriteValuePacked(ref FastBufferWriter writer, T break; } } - + /// /// Write single-precision floating point value to the buffer as a varint /// @@ -198,7 +198,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) { WriteUInt64Packed(ref writer, ToUlong(value)); } - + /// /// Write a byte to the buffer. /// @@ -206,7 +206,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); - + /// /// Write a signed byte to the buffer. /// @@ -214,7 +214,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, sbyte value) => writer.WriteByteSafe((byte)value); - + /// /// Write a bool to the buffer. /// @@ -411,7 +411,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) } #endif #endregion - + #region Bit Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -430,7 +430,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) /// /// The writer to write to /// The value to pack - public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); + public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort)Arithmetic.ZigZagEncode(value)); /// /// Writes a 15-bit unsigned short to the buffer in a bit-encoded packed format. @@ -450,7 +450,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value throw new ArgumentException("BitPacked ushorts must be <= 15 bits"); } #endif - + if (value <= 0b0111_1111) { if (!writer.VerifyCanWriteInternal(1)) @@ -460,7 +460,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value writer.WriteByte((byte)(value << 1)); return; } - + if (!writer.VerifyCanWriteInternal(2)) { throw new OverflowException("Writing past the end of the buffer"); @@ -479,7 +479,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value /// /// The writer to write to /// The value to pack - public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint) Arithmetic.ZigZagEncode(value)); + public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint)Arithmetic.ZigZagEncode(value)); /// /// Writes a 30-bit unsigned int to the buffer in a bit-encoded packed format. @@ -565,15 +565,15 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) return; } var writeBytes = BitCounter.GetUsedByteCount(value); - - if (!writer.VerifyCanWriteInternal(writeBytes+1)) + + if (!writer.VerifyCanWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); } - + // Looks like the same code as WriteUInt64Packed? // It's actually different because it will call the more efficient 32-bit version // of BytewiseUtility.GetUsedByteCount(). @@ -591,8 +591,8 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) return; } var writeBytes = BitCounter.GetUsedByteCount(value); - - if (!writer.VerifyCanWriteInternal(writeBytes+1)) + + if (!writer.VerifyCanWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -603,16 +603,16 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe uint ToUint(T value) where T : unmanaged { - uint* asUint = (uint*) &value; + uint* asUint = (uint*)&value; return *asUint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe ulong ToUlong(T value) where T : unmanaged { - ulong* asUlong = (ulong*) &value; + ulong* asUlong = (ulong*)&value; return *asUlong; } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index a5601dfd60..bc5edc3baf 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using Unity.Netcode; using UnityEngine; @@ -37,14 +37,14 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu return; } } - + var hasDeserializer = SerializationTypeTable.DeserializersPacked.TryGetValue(type, out var deserializer); if (hasDeserializer) { deserializer(ref reader, out value); return; } - + if (type.IsArray && type.HasElementType) { ReadValuePacked(ref reader, out int length); @@ -60,7 +60,7 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu value = arr; return; } - + if (type.IsEnum) { switch (Type.GetTypeCode(type)) @@ -107,7 +107,7 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu return; } } - + if (type == typeof(GameObject)) { reader.ReadValueSafe(out GameObject go); @@ -137,9 +137,9 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } #endregion - + #region Unmanaged Type Packing - + #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -194,7 +194,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value ReadUInt64Packed(ref reader, out ulong asULong); value = ToDouble(asULong); } - + /// /// Read a byte from the stream. /// @@ -212,7 +212,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) { reader.ReadByteSafe(out byte byteVal); - value = (sbyte) byteVal; + value = (sbyte)byteVal; } /// @@ -271,7 +271,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out int value) ReadUInt32Packed(ref reader, out uint readValue); value = (int)Arithmetic.ZigZagDecode(readValue); } - + /// /// Read an unsigned int (UInt32) from the stream. /// @@ -299,7 +299,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out long value) ReadUInt64Packed(ref reader, out ulong readValue); value = Arithmetic.ZigZagDecode(readValue); } - + /// /// Convenience method that reads two packed Vector3 from the ray from the stream /// @@ -434,7 +434,7 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, out strin } #endif #endregion - + #region Bit Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -463,7 +463,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out short val public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ushort value) { ushort returnValue = 0; - byte* ptr = ((byte*) &returnValue); + byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b1) + 1; if (!reader.VerifyCanReadInternal(numBytes)) @@ -478,7 +478,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us break; case 2: *ptr = *data; - *(ptr+1) = *(data+1); + *(ptr + 1) = *(data + 1); break; default: throw new InvalidOperationException("Could not read bit-packed value: impossible byte count"); @@ -498,7 +498,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out int value ReadValueBitPacked(ref reader, out uint readValue); value = (int)Arithmetic.ZigZagDecode(readValue); } - + /// /// Read a bit-packed 30-bit unsigned int from the stream. /// See BytePacker.cs for a description of the format. @@ -508,7 +508,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out int value public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out uint value) { uint returnValue = 0; - byte* ptr = ((byte*) &returnValue); + byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b11) + 1; if (!reader.VerifyCanReadInternal(numBytes)) @@ -523,18 +523,18 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui break; case 2: *ptr = *data; - *(ptr+1) = *(data+1); + *(ptr + 1) = *(data + 1); break; case 3: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); break; case 4: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); break; } @@ -552,7 +552,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out long valu ReadValueBitPacked(ref reader, out ulong readValue); value = Arithmetic.ZigZagDecode(readValue); } - + /// /// Read a bit-packed 61-bit signed long from the stream. /// See BytePacker.cs for a description of the format. @@ -562,7 +562,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out long valu public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ulong value) { ulong returnValue = 0; - byte* ptr = ((byte*) &returnValue); + byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b111) + 1; if (!reader.VerifyCanReadInternal(numBytes)) @@ -577,52 +577,52 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul break; case 2: *ptr = *data; - *(ptr+1) = *(data+1); + *(ptr + 1) = *(data + 1); break; case 3: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); break; case 4: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); break; case 5: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); break; case 6: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); - *(ptr+5) = *(data+5); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); + *(ptr + 5) = *(data + 5); break; case 7: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); - *(ptr+5) = *(data+5); - *(ptr+6) = *(data+6); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); + *(ptr + 5) = *(data + 5); + *(ptr + 6) = *(data + 6); break; case 8: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); - *(ptr+5) = *(data+5); - *(ptr+6) = *(data+6); - *(ptr+7) = *(data+7); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); + *(ptr + 5) = *(data + 5); + *(ptr + 6) = *(data + 6); + *(ptr + 7) = *(data + 7); break; } @@ -655,7 +655,7 @@ private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong valu } reader.ReadPartialValue(out value, numBytes); } - + private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value) { reader.ReadByteSafe(out byte firstByte); @@ -683,16 +683,16 @@ private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe float ToSingle(T value) where T : unmanaged { - float* asFloat = (float*) &value; + float* asFloat = (float*)&value; return *asFloat; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe double ToDouble(T value) where T : unmanaged { - double* asDouble = (double*) &value; + double* asDouble = (double*)&value; return *asDouble; } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs index 1cfda38e3c..36394addad 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -1,4 +1,4 @@ -using Unity.Collections.LowLevel.Unsafe; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { @@ -77,4 +77,4 @@ public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 6501441f71..34336fed81 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -9,12 +9,12 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferReader : IDisposable { - internal readonly unsafe byte* m_BufferPointer; - internal int m_Position; - internal readonly int m_Length; + internal readonly unsafe byte* BufferPointer; + internal int PositionInternal; + internal readonly int LengthInternal; private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal int m_AllowedReadMark; + internal int AllowedReadMark; private bool m_InBitwiseContext; #endif @@ -24,7 +24,7 @@ public struct FastBufferReader : IDisposable public int Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; + get => PositionInternal; } /// @@ -33,13 +33,13 @@ public int Position public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Length; + get => LengthInternal; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseReads(int amount) { - m_Position += amount; + PositionInternal += amount; #if DEVELOPMENT_BUILD || UNITY_EDITOR m_InBitwiseContext = false; #endif @@ -47,18 +47,18 @@ internal void CommitBitwiseReads(int amount) public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? buffer.Length : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr()+offset, m_Length); - m_BufferPointer = (byte*)bufferPtr; - m_Position = offset; + LengthInternal = Math.Max(1, length == -1 ? buffer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr() + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + PositionInternal = offset; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from an ArraySegment. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -70,21 +70,21 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in /// The offset of the buffer to start copying from public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + LengthInternal = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer.Array) { - UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from an existing byte array. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -96,21 +96,21 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + LengthInternal = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer) { - UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from an existing byte buffer. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -122,18 +122,18 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) { - m_Length = Math.Max(1, length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, buffer + offset, m_Length); - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + LengthInternal = Math.Max(1, length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from a FastBufferWriter. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -145,14 +145,14 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in /// The offset of the buffer to start copying from public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? writer.Length : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, writer.GetUnsafePtr() + offset, m_Length); - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + LengthInternal = Math.Max(1, length == -1 ? writer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, writer.GetUnsafePtr() + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } @@ -162,7 +162,7 @@ public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, /// public unsafe void Dispose() { - UnsafeUtility.Free(m_BufferPointer, m_Allocator); + UnsafeUtility.Free(BufferPointer, m_Allocator); } /// @@ -172,7 +172,7 @@ public unsafe void Dispose() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { - m_Position = Math.Min(Length, where); + PositionInternal = Math.Min(Length, where); } /// @@ -190,12 +190,12 @@ internal void MarkBytesRead(int amount) throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + amount > m_AllowedReadMark) + if (PositionInternal + amount > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - m_Position += amount; + PositionInternal += amount; } /// @@ -236,12 +236,12 @@ public bool VerifyCanRead(int bytes) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Length) + if (PositionInternal + bytes > LengthInternal) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + bytes; + AllowedReadMark = PositionInternal + bytes; #endif return true; } @@ -271,12 +271,12 @@ public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged } #endif int len = sizeof(T); - if (m_Position + len > m_Length) + if (PositionInternal + len > LengthInternal) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + len; + AllowedReadMark = PositionInternal + len; #endif return true; } @@ -298,14 +298,14 @@ internal bool VerifyCanReadInternal(int bytes) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Length) + if (PositionInternal + bytes > LengthInternal) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedReadMark) + if (PositionInternal + bytes > AllowedReadMark) { - m_AllowedReadMark = m_Position + bytes; + AllowedReadMark = PositionInternal + bytes; } #endif return true; @@ -322,7 +322,7 @@ public unsafe byte[] ToArray() byte[] ret = new byte[Length]; fixed (byte* b = ret) { - UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + UnsafeUtility.MemCpy(b, BufferPointer, Length); } return ret; } @@ -334,7 +334,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_BufferPointer; + return BufferPointer; } /// @@ -344,7 +344,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_BufferPointer + m_Position; + return BufferPointer + PositionInternal; } /// @@ -369,14 +369,14 @@ public void ReadObject(out object value, Type type, bool isNullable = false) return; } } - + var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); if (hasDeserializer) { deserializer(ref this, out value); return; } - + if (type.IsArray && type.HasElementType) { ReadValueSafe(out int length); @@ -392,7 +392,7 @@ public void ReadObject(out object value, Type type, bool isNullable = false) value = arr; return; } - + if (type.IsEnum) { switch (Type.GetTypeCode(type)) @@ -439,7 +439,7 @@ public void ReadObject(out object value, Type type, bool isNullable = false) return; } } - + if (type == typeof(GameObject)) { ReadValueSafe(out GameObject go); @@ -487,7 +487,7 @@ public void ReadNetworkSerializable(out T value) where T : INetworkSerializab public void ReadValue(out GameObject value) { ReadValue(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.gameObject; @@ -501,7 +501,7 @@ public void ReadValue(out GameObject value) value = null; } - + /// /// Read a GameObject /// @@ -512,7 +512,7 @@ public void ReadValue(out GameObject value) public void ReadValueSafe(out GameObject value) { ReadValueSafe(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.gameObject; @@ -534,7 +534,7 @@ public void ReadValueSafe(out GameObject value) public void ReadValue(out NetworkObject value) { ReadValue(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject; @@ -559,7 +559,7 @@ public void ReadValue(out NetworkObject value) public void ReadValueSafe(out NetworkObject value) { ReadValueSafe(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject; @@ -582,7 +582,7 @@ public void ReadValue(out NetworkBehaviour value) { ReadValue(out ulong networkObjectId); ReadValue(out ushort networkBehaviourId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); @@ -608,7 +608,7 @@ public void ReadValueSafe(out NetworkBehaviour value) { ReadValueSafe(out ulong networkObjectId); ReadValueSafe(out ushort networkBehaviourId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); @@ -622,7 +622,7 @@ public void ReadValueSafe(out NetworkBehaviour value) value = null; } - + /// /// Reads a string /// NOTE: ALLOCATES @@ -640,13 +640,13 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - ReadByte(out byte b); - native[i] = (char) b; + ReadByte(out byte b); + native[i] = (char)b; } } else { - ReadBytes((byte*) native, target * sizeof(char)); + ReadBytes((byte*)native, target * sizeof(char)); } } } @@ -669,14 +669,14 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(sizeof(uint))) { throw new OverflowException("Reading past the end of the buffer"); } - + ReadValue(out uint length); - + if (!VerifyCanReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) { throw new OverflowException("Reading past the end of the buffer"); @@ -689,13 +689,13 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - ReadByte(out byte b); - native[i] = (char) b; + ReadByte(out byte b); + native[i] = (char)b; } } else { - ReadBytes((byte*) native, target * sizeof(char)); + ReadBytes((byte*)native, target * sizeof(char)); } } } @@ -706,7 +706,7 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) /// /// Stores the read array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValue(out T[] array) where T: unmanaged + public unsafe void ReadValue(out T[] array) where T : unmanaged { ReadValue(out int sizeInTs); int sizeInBytes = sizeInTs * sizeof(T); @@ -727,7 +727,7 @@ public unsafe void ReadValue(out T[] array) where T: unmanaged /// /// Stores the read array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValueSafe(out T[] array) where T: unmanaged + public unsafe void ReadValueSafe(out T[] array) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -736,7 +736,7 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); @@ -754,7 +754,7 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged ReadBytes(bytes, sizeInBytes); } } - + /// /// Read a partial value. The value is zero-initialized and then the specified number of bytes is read into it. /// @@ -765,7 +765,7 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -773,18 +773,18 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + bytesToRead > m_AllowedReadMark) + if (PositionInternal + bytesToRead > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - T val = new T(); - byte* ptr = ((byte*) &val) + offsetBytes; - byte* bufferPointer = m_BufferPointer + m_Position; + var val = new T(); + byte* ptr = ((byte*)&val) + offsetBytes; + byte* bufferPointer = BufferPointer + PositionInternal; UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); - m_Position += bytesToRead; + PositionInternal += bytesToRead; value = val; } @@ -794,19 +794,19 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB /// Stores the read value [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadByte(out byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + 1 > m_AllowedReadMark) + if (PositionInternal + 1 > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - value = m_BufferPointer[m_Position++]; + value = BufferPointer[PositionInternal++]; } /// @@ -818,7 +818,7 @@ public unsafe void ReadByte(out byte value) /// Stores the read value [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadByteSafe(out byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -826,14 +826,14 @@ public unsafe void ReadByteSafe(out byte value) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(1)) { throw new OverflowException("Reading past the end of the buffer"); } - value = m_BufferPointer[m_Position++]; + value = BufferPointer[PositionInternal++]; } - + /// /// Read multiple bytes to the stream /// @@ -842,22 +842,22 @@ public unsafe void ReadByteSafe(out byte value) /// Offset of the byte buffer to store into [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBytes(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + size > m_AllowedReadMark) + if (PositionInternal + size > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); - m_Position += size; + UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); + PositionInternal += size; } - + /// /// Read multiple bytes to the stream /// @@ -869,7 +869,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) /// Offset of the byte buffer to store into [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -877,15 +877,15 @@ public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); - m_Position += size; + UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); + PositionInternal += size; } - + /// /// Read multiple bytes from the stream /// @@ -900,7 +900,7 @@ public unsafe void ReadBytes(ref byte[] value, int size, int offset = 0) ReadBytes(ptr, size, offset); } } - + /// /// Read multiple bytes from the stream /// @@ -929,26 +929,26 @@ public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) public unsafe void ReadValue(out T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + len > m_AllowedReadMark) + if (PositionInternal + len > AllowedReadMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - + fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); } - m_Position += len; + PositionInternal += len; } - + /// /// Read a value of any unmanaged type to the buffer. /// It will be copied from the buffer exactly as it existed in memory on the writing end. @@ -962,7 +962,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged public unsafe void ReadValueSafe(out T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -970,18 +970,18 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } - + fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); } - m_Position += len; + PositionInternal += len; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 55e1ba0b6b..91ac86ba91 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -9,24 +9,24 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferWriter : IDisposable { - internal unsafe byte* m_BufferPointer; - internal int m_Position; + internal unsafe byte* BufferPointer; + internal int PositionInternal; private int m_Length; - internal int m_Capacity; - internal readonly int m_MaxCapacity; + internal int CapacityInternal; + internal readonly int MaxCapacityInternal; private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal int m_AllowedWriteMark; + internal int AllowedWriteMark; private bool m_InBitwiseContext; #endif - + /// /// The current write position /// public int Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; + get => PositionInternal; } /// @@ -35,7 +35,7 @@ public int Position public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Capacity; + get => CapacityInternal; } /// @@ -44,7 +44,7 @@ public int Capacity public int MaxCapacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_MaxCapacity; + get => MaxCapacityInternal; } /// @@ -53,14 +53,14 @@ public int MaxCapacity public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position > m_Length ? m_Position : m_Length; + get => PositionInternal > m_Length ? PositionInternal : m_Length; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseWrites(int amount) { - m_Position += amount; + PositionInternal += amount; #if DEVELOPMENT_BUILD || UNITY_EDITOR m_InBitwiseContext = false; #endif @@ -78,14 +78,14 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, size); #endif - m_BufferPointer = (byte*)buffer; - m_Position = 0; + BufferPointer = (byte*)buffer; + PositionInternal = 0; m_Length = 0; - m_Capacity = size; + CapacityInternal = size; m_Allocator = allocator; - m_MaxCapacity = maxSize < size ? size : maxSize; + MaxCapacityInternal = maxSize < size ? size : maxSize; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = 0; + AllowedWriteMark = 0; m_InBitwiseContext = false; #endif } @@ -95,7 +95,7 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) /// public unsafe void Dispose() { - UnsafeUtility.Free(m_BufferPointer, m_Allocator); + UnsafeUtility.Free(BufferPointer, m_Allocator); } /// @@ -116,12 +116,12 @@ public void Seek(int where) // because position increases, and if we seek backward, length remembers // the position it was in. // Seeking forward will not update the length. - where = Math.Min(where, m_Capacity); - if (m_Position > m_Length && where < m_Position) + where = Math.Min(where, CapacityInternal); + if (PositionInternal > m_Length && where < PositionInternal) { - m_Length = m_Position; + m_Length = PositionInternal; } - m_Position = where; + PositionInternal = where; } /// @@ -137,11 +137,11 @@ public void Truncate(int where = -1) where = Position; } - if (m_Position > where) + if (PositionInternal > where) { - m_Position = where; + PositionInternal = where; } - if(m_Length > where) + if (m_Length > where) { m_Length = where; } @@ -163,15 +163,15 @@ public BitWriter EnterBitwiseContext() internal unsafe void Grow() { - var newSize = Math.Min(m_Capacity * 2, m_MaxCapacity); + var newSize = Math.Min(CapacityInternal * 2, MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, newSize); #endif - UnsafeUtility.MemCpy(buffer, m_BufferPointer, Length); - UnsafeUtility.Free(m_BufferPointer, m_Allocator); - m_BufferPointer = (byte*)buffer; - m_Capacity = newSize; + UnsafeUtility.MemCpy(buffer, BufferPointer, Length); + UnsafeUtility.Free(BufferPointer, m_Allocator); + BufferPointer = (byte*)buffer; + CapacityInternal = newSize; } /// @@ -198,9 +198,9 @@ public bool VerifyCanWrite(int bytes) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Capacity) + if (PositionInternal + bytes > CapacityInternal) { - if (m_Capacity < m_MaxCapacity) + if (CapacityInternal < MaxCapacityInternal) { Grow(); } @@ -210,11 +210,11 @@ public bool VerifyCanWrite(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = m_Position + bytes; + AllowedWriteMark = PositionInternal + bytes; #endif return true; } - + /// /// Allows faster serialization by batching bounds checking. /// When you know you will be writing multiple fields back-to-back and you know the total size, @@ -240,9 +240,9 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } #endif int len = sizeof(T); - if (m_Position + len > m_Capacity) + if (PositionInternal + len > CapacityInternal) { - if (m_Capacity < m_MaxCapacity) + if (CapacityInternal < MaxCapacityInternal) { Grow(); } @@ -252,11 +252,11 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = m_Position + len; + AllowedWriteMark = PositionInternal + len; #endif return true; } - + /// /// Internal version of VerifyCanWrite. /// Differs from VerifyCanWrite only in that it won't ever move the AllowedWriteMark backward. @@ -274,9 +274,9 @@ public bool VerifyCanWriteInternal(int bytes) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Capacity) + if (PositionInternal + bytes > CapacityInternal) { - if (m_Capacity < m_MaxCapacity) + if (CapacityInternal < MaxCapacityInternal) { Grow(); } @@ -286,9 +286,9 @@ public bool VerifyCanWriteInternal(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedWriteMark) + if (PositionInternal + bytes > AllowedWriteMark) { - m_AllowedWriteMark = m_Position + bytes; + AllowedWriteMark = PositionInternal + bytes; } #endif return true; @@ -305,7 +305,7 @@ public unsafe byte[] ToArray() byte[] ret = new byte[Length]; fixed (byte* b = ret) { - UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + UnsafeUtility.MemCpy(b, BufferPointer, Length); } return ret; } @@ -317,7 +317,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_BufferPointer; + return BufferPointer; } /// @@ -327,7 +327,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_BufferPointer + m_Position; + return BufferPointer + PositionInternal; } /// @@ -352,7 +352,7 @@ public void WriteObject(object value, bool isNullable = false) return; } } - + var type = value.GetType(); var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); if (hasSerializer) @@ -360,7 +360,7 @@ public void WriteObject(object value, bool isNullable = false) serializer(ref this, value); return; } - + if (value is Array array) { WriteValueSafe(array.Length); @@ -372,7 +372,7 @@ public void WriteObject(object value, bool isNullable = false) return; } - + if (value.GetType().IsEnum) { switch (Convert.GetTypeCode(value)) @@ -443,7 +443,7 @@ public void WriteNetworkSerializable(in T value) where T : INetworkSerializab { // TODO } - + /// /// Get the required amount of space to write a GameObject /// @@ -453,7 +453,7 @@ public static int GetWriteSize(GameObject value) { return sizeof(ulong); } - + /// /// Get the required amount of space to write a GameObject /// @@ -462,7 +462,7 @@ public static int GetGameObjectWriteSize() { return sizeof(ulong); } - + /// /// Write a GameObject /// @@ -482,7 +482,7 @@ public void WriteValue(GameObject value) WriteValue(networkObject.NetworkObjectId); } - + /// /// Write a GameObject /// @@ -505,7 +505,7 @@ public void WriteValueSafe(GameObject value) WriteValueSafe(networkObject.NetworkObjectId); } - + /// /// Get the required size to write a NetworkObject /// @@ -515,7 +515,7 @@ public static int GetWriteSize(NetworkObject value) { return sizeof(ulong); } - + /// /// Get the required size to write a NetworkObject /// @@ -555,7 +555,7 @@ public void WriteValueSafe(NetworkObject value) } WriteValueSafe(value.NetworkObjectId); } - + /// /// Get the required size to write a NetworkBehaviour /// @@ -565,8 +565,8 @@ public static int GetWriteSize(NetworkBehaviour value) { return sizeof(ulong) + sizeof(ushort); } - - + + /// /// Get the required size to write a NetworkBehaviour /// @@ -627,7 +627,7 @@ public static int GetWriteSize(string s, bool oneByteChars = false) { return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); } - + /// /// Writes a string /// @@ -641,7 +641,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - WriteByte((byte) s[i]); + WriteByte((byte)s[i]); } } else @@ -670,21 +670,21 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + int sizeInBytes = GetWriteSize(s, oneByteChars); - + if (!VerifyCanWriteInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } - + WriteValue((uint)s.Length); int target = s.Length; if (oneByteChars) { for (int i = 0; i < target; ++i) { - WriteByte((byte) s[i]); + WriteByte((byte)s[i]); } } else @@ -705,7 +705,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged + public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T : unmanaged { int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); @@ -719,7 +719,7 @@ public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = /// The amount of elements to write /// Where in the array to start [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) where T: unmanaged + public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) where T : unmanaged { int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); @@ -741,7 +741,7 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher /// The amount of elements to write /// Where in the array to start [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T: unmanaged + public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -750,10 +750,10 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); - + if (!VerifyCanWriteInternal(sizeInBytes + sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); @@ -765,7 +765,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) WriteBytes(bytes, sizeInBytes); } } - + /// /// Write a partial value. The specified number of bytes is written from the value and the rest is ignored. /// @@ -776,7 +776,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -784,17 +784,17 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + bytesToWrite > m_AllowedWriteMark) + if (PositionInternal + bytesToWrite > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - byte* ptr = ((byte*) &value) + offsetBytes; - byte* bufferPointer = m_BufferPointer + m_Position; + byte* ptr = ((byte*)&value) + offsetBytes; + byte* bufferPointer = BufferPointer + PositionInternal; UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); - m_Position += bytesToWrite; + PositionInternal += bytesToWrite; } /// @@ -803,19 +803,19 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteByte(byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + 1 > m_AllowedWriteMark) + if (PositionInternal + 1 > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - m_BufferPointer[m_Position++] = value; + BufferPointer[PositionInternal++] = value; } /// @@ -827,7 +827,7 @@ public unsafe void WriteByte(byte value) /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteByteSafe(byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -835,12 +835,12 @@ public unsafe void WriteByteSafe(byte value) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } - m_BufferPointer[m_Position++] = value; + BufferPointer[PositionInternal++] = value; } /// @@ -851,22 +851,22 @@ public unsafe void WriteByteSafe(byte value) /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + size > m_AllowedWriteMark) + if (PositionInternal + size > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); - m_Position += size; + UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); + PositionInternal += size; } - + /// /// Write multiple bytes to the stream /// @@ -878,7 +878,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -886,15 +886,15 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanWriteInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); - m_Position += size; + UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); + PositionInternal += size; } - + /// /// Write multiple bytes to the stream /// @@ -909,7 +909,7 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) WriteBytes(ptr, size, offset); } } - + /// /// Write multiple bytes to the stream /// @@ -927,7 +927,7 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) WriteBytesSafe(ptr, size, offset); } } - + /// /// Copy the contents of this writer into another writer. /// The contents will be copied from the beginning of this writer to its current position. @@ -937,9 +937,9 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyTo(FastBufferWriter other) { - other.WriteBytes(m_BufferPointer, m_Position); + other.WriteBytes(BufferPointer, PositionInternal); } - + /// /// Copy the contents of another writer into this writer. /// The contents will be copied from the beginning of the other writer to its current position. @@ -949,9 +949,9 @@ public unsafe void CopyTo(FastBufferWriter other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyFrom(FastBufferWriter other) { - WriteBytes(other.m_BufferPointer, other.m_Position); + WriteBytes(other.BufferPointer, other.PositionInternal); } - + /// /// Get the size required to write an unmanaged value /// @@ -985,26 +985,26 @@ public static unsafe int GetWriteSize() where T : unmanaged public unsafe void WriteValue(in T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + len > m_AllowedWriteMark) + if (PositionInternal + len > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - + fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); } - m_Position += len; + PositionInternal += len; } - + /// /// Write a value of any unmanaged type (including unmanaged structs) to the buffer. /// It will be copied into the buffer exactly as it exists in memory. @@ -1018,7 +1018,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged public unsafe void WriteValueSafe(in T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -1026,7 +1026,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanWriteInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); @@ -1034,9 +1034,9 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); } - m_Position += len; + PositionInternal += len; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index f421088482..9d04ce6120 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -1,7 +1,4 @@ -using System; -using System.Runtime.CompilerServices; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -24,7 +21,7 @@ public interface IBufferSerializerImplementation public void SerializeValue(ref T[] array) where T : unmanaged; public void SerializeValue(ref byte value); public void SerializeValue(ref T value) where T : unmanaged; - + // Has to have a different name to avoid conflicting with "where T: unmananged" // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables // So this is provided as an alternative to avoid boxing allocations. @@ -39,4 +36,4 @@ public interface IBufferSerializerImplementation public void SerializeValuePreChecked(ref byte value); public void SerializeValuePreChecked(ref T value) where T : unmanaged; } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index 28cc2ad1bb..c3e497281e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using UnityEngine; @@ -34,9 +34,9 @@ public static class SerializationTypeTable [typeof(float)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float)value), [typeof(double)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double)value), - + [typeof(string)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((string)value), - + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2)value), [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3)value), [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4)value), @@ -45,12 +45,12 @@ public static class SerializationTypeTable [typeof(Ray)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray)value), [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D)value), [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion)value), - + [typeof(char)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char)value), - + [typeof(bool)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool)value), - - + + [typeof(byte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((byte[])value), [typeof(sbyte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((sbyte[])value), @@ -72,12 +72,12 @@ public static class SerializationTypeTable [typeof(Ray[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray[])value), [typeof(Ray2D[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D[])value), [typeof(Quaternion[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion[])value), - + [typeof(char[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char[])value), - + [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), }; - + public static Dictionary Deserializers = new Dictionary { [typeof(byte)] = (ref FastBufferReader reader, out object value) => @@ -91,209 +91,209 @@ public static class SerializationTypeTable value = (sbyte)tmp; }, - [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ushort tmp); value = tmp; }, - [typeof(short)] = (ref FastBufferReader reader, out object value) => + [typeof(short)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out short tmp); value = tmp; }, - [typeof(uint)] = (ref FastBufferReader reader, out object value) => + [typeof(uint)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out uint tmp); value = tmp; }, - [typeof(int)] = (ref FastBufferReader reader, out object value) => + [typeof(int)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out int tmp); value = tmp; }, - [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ulong tmp); value = tmp; }, - [typeof(long)] = (ref FastBufferReader reader, out object value) => + [typeof(long)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out long tmp); value = tmp; }, - [typeof(float)] = (ref FastBufferReader reader, out object value) => + [typeof(float)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out float tmp); value = tmp; }, - [typeof(double)] = (ref FastBufferReader reader, out object value) => + [typeof(double)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out double tmp); value = tmp; }, - - [typeof(string)] = (ref FastBufferReader reader, out object value) => + + [typeof(string)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out string tmp); value = tmp; }, - - [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector2 tmp); value = tmp; }, - [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector3 tmp); value = tmp; }, - [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector4 tmp); value = tmp; }, - [typeof(Color)] = (ref FastBufferReader reader, out object value) => + [typeof(Color)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color tmp); value = tmp; }, - [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color32 tmp); value = tmp; }, - [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray tmp); value = tmp; }, - [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray2D tmp); value = tmp; }, - [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Quaternion tmp); value = tmp; }, - - [typeof(char)] = (ref FastBufferReader reader, out object value) => + + [typeof(char)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out char tmp); value = tmp; }, - - [typeof(bool)] = (ref FastBufferReader reader, out object value) => + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out bool tmp); value = tmp; }, - - - [typeof(byte[])] = (ref FastBufferReader reader, out object value) => + + + [typeof(byte[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out byte[] tmp); value = tmp; }, - [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => + [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out sbyte[] tmp); value = tmp; }, - [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => + [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ushort[] tmp); value = tmp; }, - [typeof(short[])] = (ref FastBufferReader reader, out object value) => + [typeof(short[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out short[] tmp); value = tmp; }, - [typeof(uint[])] = (ref FastBufferReader reader, out object value) => + [typeof(uint[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out uint[] tmp); value = tmp; }, - [typeof(int[])] = (ref FastBufferReader reader, out object value) => + [typeof(int[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out int[] tmp); value = tmp; }, - [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => + [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ulong[] tmp); value = tmp; }, - [typeof(long[])] = (ref FastBufferReader reader, out object value) => + [typeof(long[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out long[] tmp); value = tmp; }, - [typeof(float[])] = (ref FastBufferReader reader, out object value) => + [typeof(float[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out float[] tmp); value = tmp; }, - [typeof(double[])] = (ref FastBufferReader reader, out object value) => + [typeof(double[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out double[] tmp); value = tmp; }, - [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => + [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector2[] tmp); value = tmp; }, - [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => + [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector3[] tmp); value = tmp; }, - [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => + [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector4[] tmp); value = tmp; }, - [typeof(Color[])] = (ref FastBufferReader reader, out object value) => + [typeof(Color[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color[] tmp); value = tmp; }, - [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => + [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color32[] tmp); value = tmp; }, - [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => + [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray[] tmp); value = tmp; }, - [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => + [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray2D[] tmp); value = tmp; }, - [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => + [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Quaternion[] tmp); value = tmp; }, - - [typeof(char[])] = (ref FastBufferReader reader, out object value) => + + [typeof(char[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out char[] tmp); value = tmp; }, - - [typeof(bool[])] = (ref FastBufferReader reader, out object value) => + + [typeof(bool[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out bool[] tmp); value = tmp; @@ -314,9 +314,9 @@ public static class SerializationTypeTable [typeof(float)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (float)value), [typeof(double)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (double)value), - + [typeof(string)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (string)value), - + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector2)value), [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector3)value), [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector4)value), @@ -325,12 +325,12 @@ public static class SerializationTypeTable [typeof(Ray)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray)value), [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray2D)value), [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Quaternion)value), - + [typeof(char)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (char)value), - + [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), }; - + public static Dictionary DeserializersPacked = new Dictionary { [typeof(byte)] = (ref FastBufferReader reader, out object value) => @@ -344,106 +344,106 @@ public static class SerializationTypeTable value = (sbyte)tmp; }, - [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out ushort tmp); value = tmp; }, - [typeof(short)] = (ref FastBufferReader reader, out object value) => + [typeof(short)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out short tmp); value = tmp; }, - [typeof(uint)] = (ref FastBufferReader reader, out object value) => + [typeof(uint)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out uint tmp); value = tmp; }, - [typeof(int)] = (ref FastBufferReader reader, out object value) => + [typeof(int)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out int tmp); value = tmp; }, - [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out ulong tmp); value = tmp; }, - [typeof(long)] = (ref FastBufferReader reader, out object value) => + [typeof(long)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out long tmp); value = tmp; }, - [typeof(float)] = (ref FastBufferReader reader, out object value) => + [typeof(float)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out float tmp); value = tmp; }, - [typeof(double)] = (ref FastBufferReader reader, out object value) => + [typeof(double)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out double tmp); value = tmp; }, - - [typeof(string)] = (ref FastBufferReader reader, out object value) => + + [typeof(string)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out string tmp); value = tmp; }, - - [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Vector2 tmp); value = tmp; }, - [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Vector3 tmp); value = tmp; }, - [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Vector4 tmp); value = tmp; }, - [typeof(Color)] = (ref FastBufferReader reader, out object value) => + [typeof(Color)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Color tmp); value = tmp; }, - [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Color32 tmp); value = tmp; }, - [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Ray tmp); value = tmp; }, - [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Ray2D tmp); value = tmp; }, - [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Quaternion tmp); value = tmp; }, - - [typeof(char)] = (ref FastBufferReader reader, out object value) => + + [typeof(char)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out char tmp); value = tmp; }, - - [typeof(bool)] = (ref FastBufferReader reader, out object value) => + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out bool tmp); value = tmp; }, }; } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index 877ce2ca52..62ae287a3a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -1,8 +1,8 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace Unity.Multiplayer.Netcode { - public struct Ref where T: unmanaged + public struct Ref where T : unmanaged { private unsafe T* m_Value; @@ -22,4 +22,4 @@ public unsafe ref T Value get => ref *m_Value; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs index 623a87c008..bac5669ad9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs @@ -1,17 +1,17 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Unity.Multiplayer.Netcode { - public ref struct RefArray where T: unmanaged + public ref struct RefArray where T : unmanaged { public struct RefArrayImplementation : IReadOnlyList where T : unmanaged { - internal unsafe T* m_Value; - internal int m_Length; + private unsafe T* m_Value; + private int m_Length; internal unsafe RefArrayImplementation(T* ptr, int length) { @@ -27,13 +27,13 @@ public unsafe ref T Value public struct Enumerator : IEnumerator, IEnumerator, IDisposable { - private RefArrayImplementation m_array; + private RefArrayImplementation m_Array; private int m_Index; public Enumerator(ref RefArrayImplementation array) { - this.m_array = array; - this.m_Index = -1; + m_Array = array; + m_Index = -1; } public void Dispose() @@ -42,24 +42,24 @@ public void Dispose() public bool MoveNext() { - ++this.m_Index; - return this.m_Index < this.m_array.Length; + ++m_Index; + return m_Index < m_Array.Length; } - public void Reset() => this.m_Index = -1; + public void Reset() => m_Index = -1; - public T Current => this.m_array[this.m_Index]; + public T Current => m_Array[m_Index]; - object IEnumerator.Current => (object) this.Current; + object IEnumerator.Current => (object)Current; } - public RefArrayImplementation.Enumerator GetEnumerator() => - new RefArrayImplementation.Enumerator(ref this); + public Enumerator GetEnumerator() => + new Enumerator(ref this); IEnumerator IEnumerable.GetEnumerator() => - (IEnumerator) new RefArrayImplementation.Enumerator(ref this); + (IEnumerator)new Enumerator(ref this); - IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); public int Count => m_Length; public int Length => m_Length; @@ -71,8 +71,8 @@ public unsafe T this[int index] } } - internal RefArrayImplementation m_Value; - + private RefArrayImplementation m_Value; + public unsafe RefArray(T* ptr, int length) { m_Value = new RefArrayImplementation(ptr, length); @@ -89,4 +89,4 @@ public unsafe ref RefArrayImplementation Value } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 12331d6ff5..8584391af1 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests @@ -15,10 +15,10 @@ public void TestBitCounter64Bits() for (int i = 0; i < 64; ++i) { value = 1UL << i; - Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); } } - + [Test] public void TestBitCounter32Bits() { @@ -29,7 +29,7 @@ public void TestBitCounter32Bits() for (int i = 0; i < 32; ++i) { value = 1U << i; - Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); } } [Test] @@ -42,10 +42,10 @@ public void TestByteCounter64Bits() for (int i = 0; i < 64; ++i) { value = 1UL << i; - Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); } } - + [Test] public void TestByteCounter32Bits() { @@ -56,8 +56,8 @@ public void TestByteCounter32Bits() for (int i = 0; i < 32; ++i) { value = 1U << i; - Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index c8fa6a5c4a..a63f84c48d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using NUnit.Framework; using Unity.Collections; using Unity.Multiplayer.Netcode; @@ -10,30 +10,30 @@ public class BitReaderTests [Test] public void TestReadingOneBit() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBit(true); - + bitWriter.WriteBit(true); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); - + bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(true); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); bitWriter.WriteBit(false); bitWriter.WriteBit(true); } - + writer.WriteByte(0b11111111); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -80,11 +80,11 @@ public void TestReadingOneBit() public unsafe void TestVerifyCanReadBits() { var nativeArray = new NativeArray(4, Allocator.Temp); - FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + var reader = new FastBufferReader(nativeArray, Allocator.Temp); nativeArray.Dispose(); using (reader) { - int* asInt = (int*) reader.GetUnsafePtr(); + int* asInt = (int*)reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; using (var bitReader = reader.EnterBitwiseContext()) @@ -129,7 +129,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - + try { bitReader.ReadBits(out byteVal, 1); @@ -143,7 +143,7 @@ public unsafe void TestVerifyCanReadBits() throw e; } Assert.IsTrue(bitReader.VerifyCanReadBits(3)); - + try { bitReader.ReadBits(out byteVal, 4); @@ -159,19 +159,19 @@ public unsafe void TestVerifyCanReadBits() Assert.IsTrue(bitReader.VerifyCanReadBits(4)); bitReader.ReadBits(out byteVal, 3); Assert.AreEqual(0b010, byteVal); - + Assert.IsTrue(bitReader.VerifyCanReadBits(5)); - + bitReader.ReadBits(out byteVal, 5); Assert.AreEqual(0b10101, byteVal); } - + Assert.AreEqual(2, reader.Position); - + Assert.IsTrue(reader.VerifyCanRead(1)); reader.ReadByte(out byte nextByte); Assert.AreEqual(0b11111111, nextByte); - + Assert.IsTrue(reader.VerifyCanRead(1)); reader.ReadByte(out nextByte); Assert.AreEqual(0b00000000, nextByte); @@ -183,11 +183,11 @@ public unsafe void TestVerifyCanReadBits() } } } - + [Test] public void TestReadingMultipleBits() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -200,7 +200,7 @@ public void TestReadingMultipleBits() bitWriter.WriteBits(0b11111010, 4); } writer.WriteByte(0b11111111); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) @@ -230,11 +230,11 @@ public void TestReadingMultipleBits() } } } - + [Test] public void TestReadingMultipleBitsToLongs() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -246,9 +246,9 @@ public void TestReadingMultipleBitsToLongs() bitWriter.WriteBits(0b11111000UL, 4); bitWriter.WriteBits(0b11111010UL, 4); } - + writer.WriteByte(0b11111111); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { @@ -277,16 +277,16 @@ public void TestReadingMultipleBitsToLongs() } } } - + [Test] public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) { ulong value = 0xFFFFFFFFFFFFFFFF; - FastBufferReader reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); + var reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); using (reader) { - ulong* asUlong = (ulong*) reader.GetUnsafePtr(); - + ulong* asUlong = (ulong*)reader.GetUnsafePtr(); + Assert.AreEqual(value, *asUlong); var mask = 0UL; for (var i = 0; i < numBits; ++i) @@ -304,16 +304,16 @@ public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) Assert.AreEqual(value & mask, readValue); } } - + [Test] public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() { var nativeArray = new NativeArray(4, Allocator.Temp); - FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + var reader = new FastBufferReader(nativeArray, Allocator.Temp); nativeArray.Dispose(); using (reader) { - int* asInt = (int*) reader.GetUnsafePtr(); + int* asInt = (int*)reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; Assert.Throws(() => @@ -323,7 +323,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() bitReader.ReadBit(out bool b); } }); - + Assert.Throws(() => { using (var bitReader = reader.EnterBitwiseContext()) @@ -331,7 +331,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() bitReader.ReadBits(out byte b, 1); } }); - + Assert.Throws(() => { using (var bitReader = reader.EnterBitwiseContext()) @@ -339,7 +339,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() bitReader.ReadBits(out ulong ul, 1); } }); - + Assert.AreEqual(0, reader.Position); Assert.Throws(() => @@ -365,4 +365,4 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index d794bb6cb9..2459147dd6 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using NUnit.Framework; using Unity.Collections; using Unity.Multiplayer.Netcode; @@ -10,11 +10,11 @@ public class BitWriterTests [Test] public unsafe void TestWritingOneBit() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -22,30 +22,30 @@ public unsafe void TestWritingOneBit() { bitWriter.WriteBit(true); Assert.AreEqual(0b1, *asInt); - + bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b1011, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b10001011, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b1010_10001011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10001011, *asInt); - + writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } @@ -53,11 +53,11 @@ public unsafe void TestWritingOneBit() [Test] public unsafe void TestVerifyCanWriteBits() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); using (var bitWriter = writer.EnterBitwiseContext()) @@ -84,11 +84,11 @@ public unsafe void TestVerifyCanWriteBits() Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b1011, *asInt); - + try { bitWriter.WriteBits(0b11111111, 4); @@ -101,7 +101,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - + try { bitWriter.WriteBits(0b11111111, 1); @@ -115,7 +115,7 @@ public unsafe void TestVerifyCanWriteBits() throw e; } Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); - + try { bitWriter.WriteBits(0b11111111, 4); @@ -129,24 +129,24 @@ public unsafe void TestVerifyCanWriteBits() throw e; } Assert.IsTrue(bitWriter.VerifyCanWriteBits(4)); - + bitWriter.WriteBits(0b11111010, 3); - + Assert.AreEqual(0b00101011, *asInt); Assert.IsTrue(bitWriter.VerifyCanWriteBits(5)); - + bitWriter.WriteBits(0b11110101, 5); Assert.AreEqual(0b1010_10101011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10101011, *asInt); - + Assert.IsTrue(writer.VerifyCanWrite(1)); writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); - + Assert.IsTrue(writer.VerifyCanWrite(1)); writer.WriteByte(0b00000000); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); @@ -158,15 +158,15 @@ public unsafe void TestVerifyCanWriteBits() } } } - + [Test] public unsafe void TestWritingMultipleBits() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -174,36 +174,36 @@ public unsafe void TestWritingMultipleBits() { bitWriter.WriteBits(0b11111111, 1); Assert.AreEqual(0b1, *asInt); - + bitWriter.WriteBits(0b11111111, 1); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBits(0b11111110, 2); Assert.AreEqual(0b1011, *asInt); - + bitWriter.WriteBits(0b11111000, 4); Assert.AreEqual(0b10001011, *asInt); - + bitWriter.WriteBits(0b11111010, 4); Assert.AreEqual(0b1010_10001011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10001011, *asInt); - + writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } } - + [Test] public unsafe void TestWritingMultipleBitsFromLongs() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -211,36 +211,36 @@ public unsafe void TestWritingMultipleBitsFromLongs() { bitWriter.WriteBits(0b11111111UL, 1); Assert.AreEqual(0b1, *asInt); - + bitWriter.WriteBits(0b11111111UL, 1); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBits(0b11111110UL, 2); Assert.AreEqual(0b1011, *asInt); - + bitWriter.WriteBits(0b11111000UL, 4); Assert.AreEqual(0b10001011, *asInt); - + bitWriter.WriteBits(0b11111010UL, 4); Assert.AreEqual(0b1010_10001011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10001011, *asInt); - + writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } } - + [Test] public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) { - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); + var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); using (writer) { - ulong* asUlong = (ulong*) writer.GetUnsafePtr(); - + ulong* asUlong = (ulong*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asUlong); var mask = 0UL; for (var i = 0; i < numBits; ++i) @@ -258,15 +258,15 @@ public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) Assert.AreEqual(value & mask, *asUlong); } } - + [Test] public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.Throws(() => @@ -276,7 +276,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBit(true); } }); - + Assert.Throws(() => { using (var bitWriter = writer.EnterBitwiseContext()) @@ -284,7 +284,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBit(false); } }); - + Assert.Throws(() => { using (var bitWriter = writer.EnterBitwiseContext()) @@ -292,7 +292,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBits(0b11111111, 1); } }); - + Assert.Throws(() => { using (var bitWriter = writer.EnterBitwiseContext()) @@ -300,14 +300,14 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBits(0b11111111UL, 1); } }); - + Assert.AreEqual(0, writer.Position); Assert.AreEqual(0, *asInt); - + writer.WriteByteSafe(0b11111111); Assert.AreEqual(0b11111111, *asInt); - - + + Assert.Throws(() => { Assert.IsTrue(writer.VerifyCanWrite(1)); @@ -330,4 +330,4 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 4c6f967c00..a1bf9b53c8 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; @@ -15,19 +15,19 @@ public class BufferSerializerTests [Test] public void TestIsReaderIsWriter() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); Assert.IsFalse(serializer.IsReader); Assert.IsTrue(serializer.IsWriter); } byte[] readBuffer = new byte[4]; - FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + var reader = new FastBufferReader(readBuffer, Allocator.Temp); using (reader) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerReader(ref reader)); Assert.IsTrue(serializer.IsReader); Assert.IsFalse(serializer.IsWriter); @@ -36,10 +36,10 @@ public void TestIsReaderIsWriter() [Test] public unsafe void TestGetUnderlyingStructs() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); ref FastBufferWriter underlyingWriter = ref serializer.GetFastBufferWriter(); fixed (FastBufferWriter* ptr = &underlyingWriter) @@ -58,10 +58,10 @@ public unsafe void TestGetUnderlyingStructs() } byte[] readBuffer = new byte[4]; - FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + var reader = new FastBufferReader(readBuffer, Allocator.Temp); using (reader) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerReader(ref reader)); ref FastBufferReader underlyingReader = ref serializer.GetFastBufferReader(); fixed (FastBufferReader* ptr = &underlyingReader) @@ -79,58 +79,58 @@ public unsafe void TestGetUnderlyingStructs() } } } - + // Not reimplementing the entire suite of all value tests for BufferSerializer since they're already tested // for the underlying structures. These are just basic tests to make sure the correct underlying functions // are being called. [Test] public void TestSerializingObjects() { - Random random = new Random(); + var random = new Random(); int value = random.Next(); object asObj = value; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref asObj, typeof(int)); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); object readValue = 0; deserializer.SerializeValue(ref readValue, typeof(int)); - + Assert.AreEqual(value, readValue); } } } - + [Test] public void TestSerializingValues() { - Random random = new Random(); + var random = new Random(); int value = random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int readValue = 0; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -138,24 +138,24 @@ public void TestSerializingValues() [Test] public void TestSerializingBytes() { - Random random = new Random(); + var random = new Random(); byte value = (byte)random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); byte readValue = 0; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -163,24 +163,24 @@ public void TestSerializingBytes() [Test] public void TestSerializingArrays() { - Random random = new Random(); - int[] value = {random.Next(), random.Next(), random.Next()}; + var random = new Random(); + int[] value = { random.Next(), random.Next(), random.Next() }; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int[] readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -190,37 +190,37 @@ public void TestSerializingStrings([Values] bool oneBytChars) { string value = "I am a test string"; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value, oneBytChars); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); string readValue = null; deserializer.SerializeValue(ref readValue, oneBytChars); - + Assert.AreEqual(value, readValue); } } } - - + + [Test] public void TestSerializingValuesPreChecked() { - Random random = new Random(); + var random = new Random(); int value = random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -234,10 +234,10 @@ public void TestSerializingValuesPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); serializer.SerializeValuePreChecked(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int readValue = 0; try @@ -251,7 +251,7 @@ public void TestSerializingValuesPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -259,13 +259,13 @@ public void TestSerializingValuesPreChecked() [Test] public void TestSerializingBytesPreChecked() { - Random random = new Random(); + var random = new Random(); byte value = (byte)random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -279,10 +279,10 @@ public void TestSerializingBytesPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); serializer.SerializeValuePreChecked(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); byte readValue = 0; try @@ -296,7 +296,7 @@ public void TestSerializingBytesPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -304,13 +304,13 @@ public void TestSerializingBytesPreChecked() [Test] public void TestSerializingArraysPreChecked() { - Random random = new Random(); - int[] value = {random.Next(), random.Next(), random.Next()}; + var random = new Random(); + int[] value = { random.Next(), random.Next(), random.Next() }; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -324,10 +324,10 @@ public void TestSerializingArraysPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); serializer.SerializeValuePreChecked(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int[] readValue = null; try @@ -341,7 +341,7 @@ public void TestSerializingArraysPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -351,10 +351,10 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) { string value = "I am a test string"; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -368,10 +368,10 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); serializer.SerializeValuePreChecked(ref value, oneBytChars); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); string readValue = null; try @@ -385,12 +385,12 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); deserializer.SerializeValuePreChecked(ref readValue, oneBytChars); - + Assert.AreEqual(value, readValue); } } } - + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, NetworkObject networkObject); private void RunGameObjectTest(GameObjectTestDelegate testCode) @@ -420,101 +420,101 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } finally { - GameObject.DestroyImmediate(obj); + UnityEngine.Object.DestroyImmediate(obj); networkManager.StopServer(); } } - + [Test] public void TestSerializingGameObjects() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref obj); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); GameObject readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(obj, readValue); } } } ); } - + [Test] public void TestSerializingNetworkObjects() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref networkObject); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkObject readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(networkObject, readValue); } } } ); } - + [Test] public void TestSerializingNetworkBehaviours() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref networkBehaviour); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkBehaviour readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(networkBehaviour, readValue); } } } ); } - + [Test] public void TestSerializingGameObjectsPreChecked() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -528,10 +528,10 @@ public void TestSerializingGameObjectsPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(obj))); serializer.SerializeValuePreChecked(ref obj); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); GameObject readValue = null; try @@ -545,23 +545,23 @@ public void TestSerializingGameObjectsPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(obj, readValue); } } } ); } - + [Test] public void TestSerializingNetworkObjectsPreChecked() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -575,10 +575,10 @@ public void TestSerializingNetworkObjectsPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkObject))); serializer.SerializeValuePreChecked(ref networkObject); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkObject readValue = null; try @@ -592,23 +592,23 @@ public void TestSerializingNetworkObjectsPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(networkObject, readValue); } } } ); } - + [Test] public void TestSerializingNetworkBehavioursPreChecked() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -622,10 +622,10 @@ public void TestSerializingNetworkBehavioursPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkBehaviour))); serializer.SerializeValuePreChecked(ref networkBehaviour); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkBehaviour readValue = null; try @@ -639,7 +639,7 @@ public void TestSerializingNetworkBehavioursPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(networkBehaviour, readValue); } } @@ -647,4 +647,4 @@ public void TestSerializingNetworkBehavioursPreChecked() ); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 9ae500584c..2fcc0f4e28 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using NUnit.Framework; @@ -69,21 +69,6 @@ private enum ULongEnum : ulong C } - private struct TestStruct - { - public byte a; - public short b; - public ushort c; - public int d; - public uint e; - public long f; - public ulong g; - public bool h; - public char i; - public float j; - public double k; - } - public enum WriteType { WriteDirect, @@ -201,11 +186,11 @@ private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) Assert.AreEqual(readValue, value); } } - - private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: unmanaged + + private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T : unmanaged { - byte* asBytePointer = (byte*) &value; - byte* otherBytePointer = (byte*) &otherValue; + byte* asBytePointer = (byte*)&value; + byte* otherBytePointer = (byte*)&otherValue; for (var i = 0; i < sizeof(T); ++i) { Assert.AreEqual(asBytePointer[i], otherBytePointer[i]); @@ -214,7 +199,7 @@ private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: un private unsafe void RunTypeTest(T value) where T : unmanaged { - FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(T) * 2, Allocator.Temp); using (writer) { BytePacker.WriteValuePacked(ref writer, (dynamic)value); @@ -222,7 +207,7 @@ private unsafe void RunTypeTest(T value) where T : unmanaged using (reader) { - T outVal = new T(); + var outVal = new T(); MethodInfo method; if (value is Enum) { @@ -233,12 +218,12 @@ private unsafe void RunTypeTest(T value) where T : unmanaged else { method = typeof(ByteUnpacker).GetMethod("ReadValuePacked", - new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + new[] { typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType() }); } - object[] args = {reader, outVal}; + object[] args = { reader, outVal }; method.Invoke(null, args); - outVal = (T) args[1]; + outVal = (T)args[1]; Assert.AreEqual(value, outVal); VerifyBytewiseEquality(value, outVal); } @@ -247,7 +232,7 @@ private unsafe void RunTypeTest(T value) where T : unmanaged private unsafe void RunObjectTypeTest(T value) where T : unmanaged { - FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(T) * 2, Allocator.Temp); using (writer) { BytePacker.WriteObjectPacked(ref writer, value); @@ -257,17 +242,17 @@ private unsafe void RunObjectTypeTest(T value) where T : unmanaged ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, (T) outVal); + VerifyBytewiseEquality(value, (T)outVal); } } } - - - + + + [Test] public void TestPacking64BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -296,11 +281,11 @@ public void TestPacking64BitsUnsigned() } } } - + [Test] public void TestPacking32BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -329,11 +314,11 @@ public void TestPacking32BitsUnsigned() } } } - + [Test] public void TestPacking64BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -350,7 +335,7 @@ public void TestPacking64BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize64(ref writer, value); CheckSignedPackedValue64(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -364,7 +349,7 @@ public void TestPacking64BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize64(ref writer, value); CheckSignedPackedValue64(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -374,11 +359,11 @@ public void TestPacking64BitsSigned() } } } - + [Test] public void TestPacking32BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -395,7 +380,7 @@ public void TestPacking32BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize32(ref writer, value); CheckSignedPackedValue32(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -409,7 +394,7 @@ public void TestPacking32BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize32(ref writer, value); CheckSignedPackedValue32(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -422,7 +407,7 @@ public void TestPacking32BitsSigned() private int GetByteCount61Bits(ulong value) { - + if (value <= 0b0001_1111) { return 1; @@ -463,7 +448,7 @@ private int GetByteCount61Bits(ulong value) private int GetByteCount30Bits(uint value) { - + if (value <= 0b0011_1111) { return 1; @@ -484,7 +469,7 @@ private int GetByteCount30Bits(uint value) private int GetByteCount15Bits(ushort value) { - + if (value <= 0b0111_1111) { return 1; @@ -552,11 +537,11 @@ private short Get14BitSignedEncodedValue(ref FastBufferWriter writer) return value; } } - + [Test] public void TestBitPacking61BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -566,7 +551,7 @@ public void TestBitPacking61BitsUnsigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b111); Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); - + for (var i = 0; i < 61; ++i) { value = 1UL << i; @@ -574,9 +559,9 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(value) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1UL << i) | (1UL << j); @@ -584,7 +569,7 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(value) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); } } @@ -592,11 +577,11 @@ public void TestBitPacking61BitsUnsigned() Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); } } - + [Test] public void TestBitPacking60BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -606,7 +591,7 @@ public void TestBitPacking60BitsSigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b111); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); - + for (var i = 0; i < 61; ++i) { value = 1U << i; @@ -615,7 +600,7 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; @@ -624,9 +609,9 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1U << i) | (1U << j); @@ -635,7 +620,7 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; @@ -644,7 +629,7 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); } } @@ -652,11 +637,11 @@ public void TestBitPacking60BitsSigned() Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); } } - + [Test] public void TestBitPacking30BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -666,7 +651,7 @@ public void TestBitPacking30BitsUnsigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b11); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); - + for (var i = 0; i < 30; ++i) { value = 1U << i; @@ -674,9 +659,9 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(value) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1U << i) | (1U << j); @@ -684,7 +669,7 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(value) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); } } @@ -692,11 +677,11 @@ public void TestBitPacking30BitsUnsigned() Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1U << 30); }); } } - + [Test] public void TestBitPacking29BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -706,7 +691,7 @@ public void TestBitPacking29BitsSigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b11); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); - + for (var i = 0; i < 29; ++i) { value = 1 << i; @@ -715,7 +700,7 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; @@ -724,9 +709,9 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1 << i) | (1 << j); @@ -735,7 +720,7 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; @@ -744,17 +729,17 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); } } } } - + [Test] public void TestBitPacking15BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -764,7 +749,7 @@ public void TestBitPacking15BitsUnsigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b1); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); - + for (var i = 0; i < 15; ++i) { value = (ushort)(1U << i); @@ -772,9 +757,9 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(value) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (ushort)((1U << i) | (1U << j)); @@ -782,7 +767,7 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(value) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); } } @@ -793,7 +778,7 @@ public void TestBitPacking15BitsUnsigned() [Test] public void TestBitPacking14BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -803,7 +788,7 @@ public void TestBitPacking14BitsSigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b1); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); - + for (var i = 0; i < 14; ++i) { value = (short)(1 << i); @@ -812,7 +797,7 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; @@ -821,9 +806,9 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (short)((1 << i) | (1 << j)); @@ -832,7 +817,7 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; @@ -841,13 +826,13 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); } } } } - + [Test] public void TestPackingBasicTypes( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), @@ -862,7 +847,7 @@ public void TestPackingBasicTypes( if (testType == typeof(byte)) { - byte b = (byte) random.Next(); + byte b = (byte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(b); @@ -874,7 +859,7 @@ public void TestPackingBasicTypes( } else if (testType == typeof(sbyte)) { - sbyte sb = (sbyte) random.Next(); + sbyte sb = (sbyte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(sb); @@ -1186,13 +1171,13 @@ public void TestPackingBasicTypes( // Rays need special handling on the equality checks because the constructor normalizes direction // Which can cause slight variations in the result var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray) * 2, Allocator.Temp); using (writer) { BytePacker.WriteValuePacked(ref writer, v); @@ -1212,7 +1197,7 @@ public void TestPackingBasicTypes( { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray) * 2, Allocator.Temp); using (writer) { BytePacker.WriteObjectPacked(ref writer, v); @@ -1220,10 +1205,10 @@ public void TestPackingBasicTypes( using (reader) { ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); - Assert.AreEqual(v.origin, ((Ray) outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray) outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray) outVal).direction.y, 0.00001); - Assert.AreEqual(v.direction.z, ((Ray) outVal).direction.z, 0.00001); + Assert.AreEqual(v.origin, ((Ray)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); + Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); } } } @@ -1234,13 +1219,13 @@ public void TestPackingBasicTypes( // Rays need special handling on the equality checks because the constructor normalizes direction // Which can cause slight variations in the result var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), new Vector2((float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray2D) * 2, Allocator.Temp); using (writer) { BytePacker.WriteValuePacked(ref writer, v); @@ -1259,7 +1244,7 @@ public void TestPackingBasicTypes( { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray2D) * 2, Allocator.Temp); using (writer) { BytePacker.WriteObjectPacked(ref writer, v); @@ -1267,9 +1252,9 @@ public void TestPackingBasicTypes( using (reader) { ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); - Assert.AreEqual(v.origin, ((Ray2D) outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray2D) outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray2D) outVal).direction.y, 0.00001); + Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 9cb416300c..e4c78be467 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; using UnityEngine; using UnityEngine.SceneManagement; @@ -62,22 +61,22 @@ private enum ULongEnum : ulong B, C }; - + private struct TestStruct { - public byte a; - public short b; - public ushort c; - public int d; - public uint e; - public long f; - public ulong g; - public bool h; - public char i; - public float j; - public double k; + public byte A; + public short B; + public ushort C; + public int D; + public uint E; + public long F; + public ulong G; + public bool H; + public char I; + public float J; + public double K; } - + public enum WriteType { WriteDirect, @@ -87,58 +86,58 @@ public enum WriteType #endregion #region Common Checks - private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); - Assert.AreEqual(writeSize+1, writer.Position, failMessage); - Assert.AreEqual(writeSize+1, writer.Length, failMessage); + Assert.AreEqual(writeSize + 1, writer.Position, failMessage); + Assert.AreEqual(writeSize + 1, writer.Length, failMessage); writer.WriteValue((byte)0xFF); - Assert.AreEqual(writeSize+2, writer.Position, failMessage); - Assert.AreEqual(writeSize+2, writer.Length, failMessage); + Assert.AreEqual(writeSize + 2, writer.Position, failMessage); + Assert.AreEqual(writeSize + 2, writer.Length, failMessage); } private void VerifyCheckBytes(ref FastBufferReader reader, int checkPosition, string failMessage = "") { reader.Seek(checkPosition); reader.VerifyCanRead(2); - + reader.ReadByte(out byte value); Assert.AreEqual(0x80, value, failMessage); reader.ReadByte(out value); Assert.AreEqual(0xFF, value, failMessage); } - + private void VerifyPositionAndLength(ref FastBufferReader reader, int length, string failMessage = "") { Assert.AreEqual(0, reader.Position, failMessage); Assert.AreEqual(length, reader.Length, failMessage); } - private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T : unmanaged { WriteCheckBytes(ref writer, writeSize, failMessage); - - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); - + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + VerifyPositionAndLength(ref reader, writer.Length, failMessage); VerifyCheckBytes(ref reader, writeSize, failMessage); - + reader.Seek(0); return reader; } #endregion - + #region Generic Checks private unsafe void RunTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); Assert.AreEqual(sizeof(T), writeSize); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -159,9 +158,9 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -179,13 +178,13 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged } } } - + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -202,7 +201,7 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T: unmanaged + private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T : unmanaged { Assert.AreEqual(value.Length, compareValue.Length); @@ -215,7 +214,7 @@ private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) whe private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); @@ -242,7 +241,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); @@ -250,7 +249,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged writer.WriteValueSafe(valueToTest); WriteCheckBytes(ref writer, writeSize); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { @@ -268,22 +267,22 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag - FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); WriteCheckBytes(ref writer, writeSize + 1); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { VerifyPositionAndLength(ref reader, writer.Length); reader.ReadObject(out object result, typeof(T[])); - VerifyArrayEquality(valueToTest, (T[]) result, 0); + VerifyArrayEquality(valueToTest, (T[])result, 0); VerifyCheckBytes(ref reader, writeSize + 1); } @@ -298,19 +297,19 @@ private TestStruct GetTestStruct() var testStruct = new TestStruct { - a = (byte) random.Next(), - b = (short) random.Next(), - c = (ushort) random.Next(), - d = (int) random.Next(), - e = (uint) random.Next(), - f = ((long) random.Next() << 32) + random.Next(), - g = ((ulong) random.Next() << 32) + (ulong) random.Next(), - h = true, - i = '\u263a', - j = (float) random.NextDouble(), - k = random.NextDouble(), + A = (byte)random.Next(), + B = (short)random.Next(), + C = (ushort)random.Next(), + D = (int)random.Next(), + E = (uint)random.Next(), + F = ((long)random.Next() << 32) + random.Next(), + G = ((ulong)random.Next() << 32) + (ulong)random.Next(), + H = true, + I = '\u263a', + J = (float)random.NextDouble(), + K = random.NextDouble(), }; - + return testStruct; } #endregion @@ -330,7 +329,7 @@ public void TestReadingBasicTypes( if (testType == typeof(byte)) { - byte b = (byte) random.Next(); + byte b = (byte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(b); @@ -346,7 +345,7 @@ public void TestReadingBasicTypes( } else if (testType == typeof(sbyte)) { - sbyte sb = (sbyte) random.Next(); + sbyte sb = (sbyte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(sb); @@ -760,7 +759,7 @@ public void TestReadingBasicTypes( else if (testType == typeof(Ray)) { var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -778,7 +777,7 @@ public void TestReadingBasicTypes( else if (testType == typeof(Ray2D)) { var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), new Vector2((float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -1479,7 +1478,7 @@ public void TestReadingStructAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => { @@ -1524,7 +1523,7 @@ public void TestReadingStructArrayAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => { @@ -1554,8 +1553,8 @@ public void TestReadingString() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); @@ -1583,8 +1582,8 @@ public void TestReadingStringSafe() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest); @@ -1610,12 +1609,12 @@ public void TestReadingStringAsObject() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { writer.WriteObject(valueToTest); - WriteCheckBytes(ref writer, serializedValueSize+1); + WriteCheckBytes(ref writer, serializedValueSize + 1); var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) @@ -1636,8 +1635,8 @@ public void TestReadingOneByteString() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest, true); @@ -1664,8 +1663,8 @@ public void TestReadingOneByteStringSafe() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest, true); @@ -1688,8 +1687,8 @@ public void TestReadingOneByteStringSafe() public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); @@ -1718,12 +1717,12 @@ public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulo } [Test] - public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); - + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); @@ -1789,7 +1788,7 @@ public void TestThrowingIfBoundsCheckingSkipped() using (writer) { Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; Assert.Throws(() => { emptyReader.ReadBytes(ref bytes, bytes.Length); }); int i = 1; Assert.Throws(() => { emptyReader.ReadValue(out int i); }); @@ -1822,14 +1821,14 @@ public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() using (writer) { writer.VerifyCanWrite(100); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; int i = 1; writer.WriteByte(1); writer.WriteBytes(bytes, bytes.Length); writer.WriteValue(i); writer.WriteValue(bytes); writer.WriteValue(""); - + writer.WriteByteSafe(1); writer.WriteBytesSafe(bytes, bytes.Length); writer.WriteValueSafe(i); @@ -1960,11 +1959,11 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } finally { - GameObject.DestroyImmediate(obj); + UnityEngine.Object.DestroyImmediate(obj); networkManager.StopServer(); } } - + [Test] public void TestNetworkBehaviour() { @@ -2040,7 +2039,7 @@ public void TestGameObject() } }); } - + [Test] public void TestNetworkBehaviourSafe() { @@ -2106,7 +2105,7 @@ public void TestGameObjectSafe() } }); } - + [Test] public void TestNetworkBehaviourAsObject() { @@ -2184,10 +2183,10 @@ public void TestVerifyInternalDoesntReduceAllowedWritePoint() { reader.VerifyCanRead(25); reader.VerifyCanReadInternal(5); - Assert.AreEqual(reader.m_AllowedReadMark, 25); + Assert.AreEqual(reader.AllowedReadMark, 25); } } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 5401a4e5f5..8fbe632431 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; using UnityEngine; using UnityEngine.SceneManagement; @@ -62,22 +61,22 @@ private enum ULongEnum : ulong B, C }; - + private struct TestStruct { - public byte a; - public short b; - public ushort c; - public int d; - public uint e; - public long f; - public ulong g; - public bool h; - public char i; - public float j; - public double k; + public byte A; + public short B; + public ushort C; + public int D; + public uint E; + public long F; + public ulong G; + public bool H; + public char I; + public float J; + public double K; } - + public enum WriteType { WriteDirect, @@ -87,35 +86,35 @@ public enum WriteType #endregion #region Common Checks - private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); - Assert.AreEqual(writeSize+1, writer.Position, failMessage); - Assert.AreEqual(writeSize+1, writer.Length, failMessage); + Assert.AreEqual(writeSize + 1, writer.Position, failMessage); + Assert.AreEqual(writeSize + 1, writer.Length, failMessage); writer.WriteValue((byte)0xFF); - Assert.AreEqual(writeSize+2, writer.Position, failMessage); - Assert.AreEqual(writeSize+2, writer.Length, failMessage); + Assert.AreEqual(writeSize + 2, writer.Position, failMessage); + Assert.AreEqual(writeSize + 2, writer.Length, failMessage); } private void VerifyCheckBytes(byte[] underlyingArray, int writeSize, string failMessage = "") { Assert.AreEqual(0x80, underlyingArray[writeSize], failMessage); - Assert.AreEqual(0xFF, underlyingArray[writeSize+1], failMessage); + Assert.AreEqual(0xFF, underlyingArray[writeSize + 1], failMessage); } - private unsafe void VerifyBytewiseEquality(T value, byte[] underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged + private unsafe void VerifyBytewiseEquality(T value, byte[] underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T : unmanaged { - byte* asBytePointer = (byte*) &value; + byte* asBytePointer = (byte*)&value; for (var i = 0; i < size; ++i) { - Assert.AreEqual(asBytePointer[i+valueOffset], underlyingArray[i+bufferOffset], failMessage); + Assert.AreEqual(asBytePointer[i + valueOffset], underlyingArray[i + bufferOffset], failMessage); } } - private unsafe void VerifyTypedEquality(T value, byte* unsafePtr) where T: unmanaged + private unsafe void VerifyTypedEquality(T value, byte* unsafePtr) where T : unmanaged { - T* checkValue = (T*) unsafePtr; + var checkValue = (T*)unsafePtr; Assert.AreEqual(value, *checkValue); } @@ -125,15 +124,15 @@ private void VerifyPositionAndLength(ref FastBufferWriter writer, int position, Assert.AreEqual(position, writer.Length, failMessage); } - private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T : unmanaged { - + VerifyPositionAndLength(ref writer, writeSize, failMessage); - + WriteCheckBytes(ref writer, writeSize, failMessage); - + var underlyingArray = writer.ToArray(); - + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, writeSize, failMessage); VerifyCheckBytes(underlyingArray, writeSize, failMessage); @@ -141,7 +140,7 @@ private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, VerifyTypedEquality(valueToTest, writer.GetUnsafePtr()); } #endregion - + #region Generic Checks private unsafe void RunTypeTest(T valueToTest) where T : unmanaged { @@ -149,10 +148,10 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged var alternateWriteSize = FastBufferWriter.GetWriteSize(); Assert.AreEqual(sizeof(T), writeSize); Assert.AreEqual(sizeof(T), alternateWriteSize); - - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -167,9 +166,9 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -180,13 +179,13 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged CommonChecks(ref writer, valueToTest, writeSize, failMessage); } } - + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -197,14 +196,14 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged { int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); fixed (T* asTPointer = value) { - T* underlyingTArray = (T*) (unsafePtr + sizeof(int) + offset); + var underlyingTArray = (T*)(unsafePtr + sizeof(int) + offset); for (var i = 0; i < value.Length; ++i) { Assert.AreEqual(asTPointer[i], underlyingTArray[i]); @@ -215,7 +214,7 @@ private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offse private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { @@ -237,7 +236,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { @@ -259,10 +258,10 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag - FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using (writer) { - + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); @@ -286,19 +285,19 @@ private TestStruct GetTestStruct() var testStruct = new TestStruct { - a = (byte) random.Next(), - b = (short) random.Next(), - c = (ushort) random.Next(), - d = (int) random.Next(), - e = (uint) random.Next(), - f = ((long) random.Next() << 32) + random.Next(), - g = ((ulong) random.Next() << 32) + (ulong) random.Next(), - h = true, - i = '\u263a', - j = (float) random.NextDouble(), - k = random.NextDouble(), + A = (byte)random.Next(), + B = (short)random.Next(), + C = (ushort)random.Next(), + D = (int)random.Next(), + E = (uint)random.Next(), + F = ((long)random.Next() << 32) + random.Next(), + G = ((ulong)random.Next() << 32) + (ulong)random.Next(), + H = true, + I = '\u263a', + J = (float)random.NextDouble(), + K = random.NextDouble(), }; - + return testStruct; } #endregion @@ -318,7 +317,7 @@ public void TestWritingBasicTypes( if (testType == typeof(byte)) { - byte b = (byte) random.Next(); + byte b = (byte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(b); @@ -334,7 +333,7 @@ public void TestWritingBasicTypes( } else if (testType == typeof(sbyte)) { - sbyte sb = (sbyte) random.Next(); + sbyte sb = (sbyte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(sb); @@ -748,7 +747,7 @@ public void TestWritingBasicTypes( else if (testType == typeof(Ray)) { var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -766,7 +765,7 @@ public void TestWritingBasicTypes( else if (testType == typeof(Ray2D)) { var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), new Vector2((float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -1467,7 +1466,7 @@ public void TestWritingStructAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; try { @@ -1506,7 +1505,7 @@ public void TestWritingStructArrayAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; try { @@ -1530,8 +1529,8 @@ public unsafe void TestWritingString() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); @@ -1540,12 +1539,12 @@ public unsafe void TestWritingString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); @@ -1564,8 +1563,8 @@ public unsafe void TestWritingStringSafe() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest); @@ -1573,12 +1572,12 @@ public unsafe void TestWritingStringSafe() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) writer.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*)((byte*)writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); @@ -1597,8 +1596,8 @@ public unsafe void TestWritingStringAsObject() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { writer.WriteObject(valueToTest); @@ -1606,12 +1605,12 @@ public unsafe void TestWritingStringAsObject() VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); - int* sizeValue = (int*) (writer.GetUnsafePtr() + sizeof(byte)); + int* sizeValue = (int*)(writer.GetUnsafePtr() + sizeof(byte)); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); + char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); @@ -1629,8 +1628,8 @@ public unsafe void TestWritingOneByteString() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); @@ -1639,7 +1638,7 @@ public unsafe void TestWritingOneByteString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) @@ -1647,7 +1646,7 @@ public unsafe void TestWritingOneByteString() byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { - Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); } } @@ -1662,8 +1661,8 @@ public unsafe void TestWritingOneByteStringSafe() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest, true); @@ -1671,7 +1670,7 @@ public unsafe void TestWritingOneByteStringSafe() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) @@ -1679,7 +1678,7 @@ public unsafe void TestWritingOneByteStringSafe() byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { - Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); } } @@ -1692,8 +1691,8 @@ public unsafe void TestWritingOneByteStringSafe() public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); using (writer) { @@ -1713,18 +1712,18 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) writer.GetUnsafePtr(); + ulong* checkValue = (ulong*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); } } [Test] - public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); - + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { @@ -1744,7 +1743,7 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) writer.GetUnsafePtr(); + ulong* checkValue = (ulong*)writer.GetUnsafePtr(); Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); } } @@ -1762,7 +1761,7 @@ public void TestToArray() writer.WriteValue(testStruct); var array = writer.ToArray(); var underlyingArray = writer.ToArray(); - for(var i = 0; i < array.Length; ++i) + for (var i = 0; i < array.Length; ++i) { Assert.AreEqual(array[i], underlyingArray[i]); } @@ -1777,7 +1776,7 @@ public void TestThrowingIfBoundsCheckingSkipped() using (writer) { Assert.Throws(() => { writer.WriteByte(1); }); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); int i = 1; Assert.Throws(() => { writer.WriteValue(i); }); @@ -1801,7 +1800,7 @@ public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() using (writer) { writer.VerifyCanWrite(100); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; int i = 1; using (var context = writer.EnterBitwiseContext()) { @@ -1810,7 +1809,7 @@ public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() Assert.Throws(() => { writer.WriteValue(i); }); Assert.Throws(() => { writer.WriteValue(bytes); }); Assert.Throws(() => { writer.WriteValue(""); }); - + Assert.Throws(() => { writer.WriteByteSafe(1); }); Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); Assert.Throws(() => { writer.WriteValueSafe(i); }); @@ -1822,7 +1821,7 @@ public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() writer.WriteValue(i); writer.WriteValue(bytes); writer.WriteValue(""); - + writer.WriteByteSafe(1); writer.WriteBytesSafe(bytes, bytes.Length); writer.WriteValueSafe(i); @@ -1880,7 +1879,7 @@ public void TestSeeking() Assert.AreEqual(writer.Position, 4); Assert.AreEqual(writer.Length, 10); - var expected = new byte[] {1, 3, 2, 5, 4, 0}; + var expected = new byte[] { 1, 3, 2, 5, 4, 0 }; var underlyingArray = writer.ToArray(); for (var i = 0; i < expected.Length; ++i) { @@ -1902,7 +1901,7 @@ public void TestTruncate() writer.Seek(5); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 10); - + writer.Truncate(8); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 8); @@ -1927,7 +1926,7 @@ public void TestGrowth() var writer = new FastBufferWriter(150, Allocator.Temp); var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); Assert.AreEqual(150, writer.Capacity); - using (writer) + using (writer) using (growingWriter) { var testStruct = GetTestStruct(); @@ -1935,7 +1934,7 @@ public void TestGrowth() writer.WriteValue(testStruct); growingWriter.VerifyCanWriteValue(testStruct); growingWriter.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); @@ -1944,10 +1943,10 @@ public void TestGrowth() // First writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); Assert.Throws(() => writer.WriteValue(testStruct)); - + // Second writer is allowed to grow Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - + // First writer shouldn't have grown Assert.AreEqual(150, writer.Capacity); Assert.AreEqual(preGrowthLength, writer.ToArray().Length); @@ -1955,28 +1954,28 @@ public void TestGrowth() Assert.AreEqual(300, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); - + // Write right up to the very end of the buffer, verify it doesn't grow growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); Assert.AreEqual(300, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); - + // Go to the end of the buffer and grow again growingWriter.Seek(300); Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); growingWriter.WriteValue(testStruct); - + // Second growth caps it at maxSize Assert.AreEqual(500, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - + VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); // Verify the growth properly copied the existing data VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300 - FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); } } @@ -2009,11 +2008,11 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } finally { - GameObject.DestroyImmediate(obj); + UnityEngine.Object.DestroyImmediate(obj); networkManager.StopHost(); } } - + [Test] public void TestNetworkBehaviour() { @@ -2069,7 +2068,7 @@ public void TestGameObject() } }); } - + [Test] public void TestNetworkBehaviourSafe() { @@ -2119,7 +2118,7 @@ public void TestGameObjectSafe() } }); } - + [Test] public void TestNetworkBehaviourAsObject() { @@ -2135,7 +2134,7 @@ public void TestNetworkBehaviourAsObject() Assert.AreEqual(0, writer.ToArray()[0]); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong)+1, sizeof(ushort)); + sizeof(ulong) + 1, sizeof(ushort)); } }); } @@ -2184,9 +2183,9 @@ public void TestVerifyInternalDoesntReduceAllowedWritePoint() { writer.VerifyCanWrite(25); writer.VerifyCanWriteInternal(5); - Assert.AreEqual(writer.m_AllowedWriteMark, 25); + Assert.AreEqual(writer.AllowedWriteMark, 25); } } #endregion } -} \ No newline at end of file +} diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs index 8bfae869b2..508d5a341b 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs @@ -11,7 +11,7 @@ namespace TestProject.RuntimeTests { - public class NetworkSceneManagerTests: BaseMultiInstanceTest + public class NetworkSceneManagerTests : BaseMultiInstanceTest { protected override int NbClients => 9; @@ -224,15 +224,15 @@ private void SetClientWaitDone(List clients) private bool ContainsAllClients(List clients) { // First, make sure we have the expected client count - if(clients.Count != m_ShouldWaitList.Count) + if (clients.Count != m_ShouldWaitList.Count) { return false; } // Next, make sure we have all client identifiers - foreach(var sceneTestInfo in m_ShouldWaitList) + foreach (var sceneTestInfo in m_ShouldWaitList) { - if(!clients.Contains(sceneTestInfo.ClientId)) + if (!clients.Contains(sceneTestInfo.ClientId)) { return false; } @@ -248,12 +248,12 @@ private bool ContainsAllClients(List clients) /// private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) { - switch(sceneEvent.SceneEventType) + switch (sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Load: case SceneEventData.SceneEventTypes.S2C_Unload: { - Assert.AreEqual(sceneEvent.SceneName,m_CurrentSceneName); + Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); Assert.IsNotNull(sceneEvent.AsyncOperation); break; From 58db1bd514a04490ae165fb3c041c2765c5629c9 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:59:12 -0500 Subject: [PATCH 15/58] Removed accidentally added files. --- testproject/Assets/StreamingAssets/BuildInfo.json | 1 - testproject/Assets/StreamingAssets/BuildInfo.json.meta | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json delete mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json.meta diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json b/testproject/Assets/StreamingAssets/BuildInfo.json deleted file mode 100644 index 14f9fcbcd2..0000000000 --- a/testproject/Assets/StreamingAssets/BuildInfo.json +++ /dev/null @@ -1 +0,0 @@ -{"BuildPath":"C:\\Users\\jaedyn.draper\\repos\\mlapi\\testproject\\Builds\\MultiprocessTests\\MultiprocessTestPlayer","IsDebug":false} \ No newline at end of file diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json.meta b/testproject/Assets/StreamingAssets/BuildInfo.json.meta deleted file mode 100644 index 5af63d794e..0000000000 --- a/testproject/Assets/StreamingAssets/BuildInfo.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 1c16680acd9943944a6d4ae05644bfd5 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From c8273398e9e37024fbf5aeafbc5b505feb86de52 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:59:46 -0500 Subject: [PATCH 16/58] Added BuildInfo.json to the .gitignore so I stop accidentally checking it in. --- testproject/Assets/StreamingAssets/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testproject/Assets/StreamingAssets/.gitignore diff --git a/testproject/Assets/StreamingAssets/.gitignore b/testproject/Assets/StreamingAssets/.gitignore new file mode 100644 index 0000000000..3b64b774a1 --- /dev/null +++ b/testproject/Assets/StreamingAssets/.gitignore @@ -0,0 +1,2 @@ +/BuildInfo.json +/BuildInfo.json.meta From f8bfb2f80e71d211ff07d5872d0c03d6ba5f9b02 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 25 Aug 2021 20:21:12 -0500 Subject: [PATCH 17/58] Addressed most of the review feedback. Still need to do a little more restructuring of some of the other tests. --- .../Runtime/Serialization/BitReader.cs | 24 +- .../Runtime/Serialization/BitWriter.cs | 38 +- .../Serialization/BufferSerializerReader.cs | 2 +- .../Serialization/BufferSerializerWriter.cs | 2 +- .../Runtime/Serialization/BytePacker.cs | 14 +- .../Runtime/Serialization/ByteUnpacker.cs | 10 +- .../Runtime/Serialization/FastBufferReader.cs | 334 +-- .../FastBufferReaderExtensions.cs | 287 +++ .../FastBufferReaderExtensions.cs.meta | 11 + .../Runtime/Serialization/FastBufferWriter.cs | 341 +-- .../FastBufferWriterExtensions.cs | 298 +++ .../FastBufferWriterExtensions.cs.meta | 11 + .../IBufferSerializerImplementation.cs | 44 +- .../BaseFastBufferReaderWriterTest.cs | 669 +++++ .../BaseFastBufferReaderWriterTest.cs.meta | 3 + .../Editor/Serialization/BitCounterTests.cs | 75 +- .../Editor/Serialization/BitReaderTests.cs | 44 +- .../Editor/Serialization/BitWriterTests.cs | 38 +- .../Serialization/BufferSerializerTests.cs | 12 +- .../Editor/Serialization/BytePackerTests.cs | 20 +- .../Serialization/FastBufferReaderTests.cs | 2225 ++++------------ .../Serialization/FastBufferWriterTests.cs | 2275 +++++------------ testproject/.gitignore | 2 + .../Tests/Runtime/NetworkSceneManagerTests.cs | 12 +- 24 files changed, 2695 insertions(+), 4096 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 01ddbc6b91..3540ea73fd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -65,7 +65,7 @@ public void Dispose() /// /// Number of bits you want to read, in total /// True if you can read, false if that would exceed buffer bounds - public bool VerifyCanReadBits(int bitCount) + public bool TryBeginReadBits(uint bitCount) { var newBitPosition = m_BitPosition + bitCount; var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; @@ -80,7 +80,7 @@ public bool VerifyCanReadBits(int bitCount) return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseReadMark = newBitPosition; + m_AllowedBitwiseReadMark = (int)newBitPosition; #endif return true; } @@ -90,7 +90,7 @@ public bool VerifyCanReadBits(int bitCount) /// /// Value to store bits into. /// Amount of bits to read - public unsafe void ReadBits(out ulong value, int bitCount) + public unsafe void ReadBits(out ulong value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (bitCount > 64) @@ -103,15 +103,15 @@ public unsafe void ReadBits(out ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); } - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); } #endif ulong val = 0; - int wholeBytes = bitCount / k_BitsPerByte; + int wholeBytes = (int)bitCount / k_BitsPerByte; byte* asBytes = (byte*)&val; if (BitAligned) { @@ -128,7 +128,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) } } - val |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); + val |= (ulong)ReadByteBits((int)bitCount & 7) << ((int)bitCount & ~7); value = val; } @@ -137,16 +137,16 @@ public unsafe void ReadBits(out ulong value, int bitCount) /// /// Value to store bits into. /// Amount of bits to read. - public void ReadBits(out byte value, int bitCount) + public void ReadBits(out byte value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); } #endif - value = ReadByteBits(bitCount); + value = ReadByteBits((int)bitCount); } /// @@ -160,7 +160,7 @@ public unsafe void ReadBit(out bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 903b279f76..3183f6ce33 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -55,15 +55,20 @@ public void Dispose() } /// - /// Verifies the requested bit count can be written to the buffer. - /// This exists as a separate method to allow multiple bit writes to be bounds checked with a single call. - /// If it returns false, you may not write, and in editor and development builds, attempting to do so will - /// throw an exception. In release builds, attempting to do so will write to random memory addresses and cause - /// Bad Things(TM). + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call TryBeginWriteBits() once on the total size, and then follow it with calls to + /// WriteBit() or WriteBits(). + /// + /// Bitwise write operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using TryBeginWriteBits(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following + /// operations in release builds. Instead, attempting to write past the marked position in release builds + /// will write to random memory and cause undefined behavior, likely including instability and crashes. /// /// Number of bits you want to write, in total /// True if you can write, false if that would exceed buffer bounds - public unsafe bool VerifyCanWriteBits(int bitCount) + public unsafe bool TryBeginWriteBits(int bitCount) { var newBitPosition = m_BitPosition + bitCount; var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; @@ -96,7 +101,7 @@ public unsafe bool VerifyCanWriteBits(int bitCount) /// /// Value to get bits from. /// Amount of bits to write - public unsafe void WriteBits(ulong value, int bitCount) + public unsafe void WriteBits(ulong value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (bitCount > 64) @@ -104,19 +109,14 @@ public unsafe void WriteBits(ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write more than 64 bits from a 64-bit value!"); } - if (bitCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); - } - - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); } #endif - int wholeBytes = bitCount / k_BitsPerByte; + int wholeBytes = (int)bitCount / k_BitsPerByte; byte* asBytes = (byte*)&value; if (BitAligned) { @@ -144,13 +144,13 @@ public unsafe void WriteBits(ulong value, int bitCount) /// /// Value to get bits from. /// Amount of bits to write. - public void WriteBits(byte value, int bitCount) + public void WriteBits(byte value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); } #endif @@ -171,7 +171,7 @@ public unsafe void WriteBit(bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index 146adc6cff..495212dfa9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -78,7 +78,7 @@ public void SerializeNetworkSerializable(ref T value) where T : INetworkSeria public bool PreCheck(int amount) { - return m_Reader.Value.VerifyCanRead(amount); + return m_Reader.Value.TryBeginRead(amount); } public void SerializeValuePreChecked(ref GameObject value) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 8349ea0d3c..f6f63b95c2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -78,7 +78,7 @@ public void SerializeNetworkSerializable(ref T value) where T : INetworkSeria public bool PreCheck(int amount) { - return m_Writer.Value.VerifyCanWrite(amount); + return m_Writer.Value.TryBeginWrite(amount); } public void SerializeValuePreChecked(ref GameObject value) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index c0e8672179..39f031d220 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -98,7 +98,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, } if (value is GameObject) { - var networkObject = ((GameObject)value).GetComponent(); + ((GameObject)value).TryGetComponent(out var networkObject); if (networkObject == null) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); @@ -453,7 +453,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value if (value <= 0b0111_1111) { - if (!writer.VerifyCanWriteInternal(1)) + if (!writer.TryBeginWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -461,7 +461,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value return; } - if (!writer.VerifyCanWriteInternal(2)) + if (!writer.TryBeginWriteInternal(2)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -501,7 +501,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) #endif value <<= 2; var numBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(numBytes)) + if (!writer.TryBeginWriteInternal(numBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -541,7 +541,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) #endif value <<= 3; var numBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(numBytes)) + if (!writer.TryBeginWriteInternal(numBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -566,7 +566,7 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) } var writeBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(writeBytes + 1)) + if (!writer.TryBeginWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -592,7 +592,7 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) } var writeBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(writeBytes + 1)) + if (!writer.TryBeginWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index bc5edc3baf..b83bca1062 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -466,7 +466,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b1) + 1; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -511,7 +511,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b11) + 1; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -565,7 +565,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b111) + 1; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -649,7 +649,7 @@ private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong valu } var numBytes = firstByte - 247; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -673,7 +673,7 @@ private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value } var numBytes = firstByte - 247; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 34336fed81..31493bc2e6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -192,7 +192,7 @@ internal void MarkBytesRead(int amount) } if (PositionInternal + amount > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif PositionInternal += amount; @@ -215,19 +215,19 @@ public BitReader EnterBitwiseContext() /// /// Allows faster serialization by batching bounds checking. /// When you know you will be reading multiple fields back-to-back and you know the total size, - /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// you can call TryBeginRead() once on the total size, and then follow it with calls to /// ReadValue() instead of ReadValueSafe() for faster serialization. /// /// Unsafe read operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following /// operations in release builds. /// /// Amount of bytes to read /// True if the read is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanRead(int bytes) + public bool TryBeginRead(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -249,19 +249,19 @@ public bool VerifyCanRead(int bytes) /// /// Allows faster serialization by batching bounds checking. /// When you know you will be reading multiple fields back-to-back and you know the total size, - /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// you can call TryBeginRead() once on the total size, and then follow it with calls to /// ReadValue() instead of ReadValueSafe() for faster serialization. /// /// Unsafe read operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following /// operations in release builds. /// /// The value you want to read /// True if the read is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged + public unsafe bool TryBeginReadValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -282,14 +282,14 @@ public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged } /// - /// Internal version of VerifyCanRead. - /// Differs from VerifyCanRead only in that it won't ever move the AllowedReadMark backward. + /// Internal version of TryBeginRead. + /// Differs from TryBeginRead only in that it won't ever move the AllowedReadMark backward. /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool VerifyCanReadInternal(int bytes) + internal bool TryBeginReadInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -347,282 +347,6 @@ public unsafe byte[] ToArray() return BufferPointer + PositionInternal; } - /// - /// Reads a boxed object in a standard format - /// Named differently from other ReadValue methods to avoid accidental boxing - /// - /// The object to read - /// The type to be read - /// - /// If true, reads a byte indicating whether or not the object is null. - /// Should match the way the object was written. - /// - public void ReadObject(out object value, Type type, bool isNullable = false) - { - if (isNullable || type.IsNullable()) - { - ReadValueSafe(out bool isNull); - - if (isNull) - { - value = null; - return; - } - } - - var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); - if (hasDeserializer) - { - deserializer(ref this, out value); - return; - } - - if (type.IsArray && type.HasElementType) - { - ReadValueSafe(out int length); - - var arr = Array.CreateInstance(type.GetElementType(), length); - - for (int i = 0; i < length; i++) - { - ReadObject(out object item, type.GetElementType()); - arr.SetValue(item, i); - } - - value = arr; - return; - } - - if (type.IsEnum) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Boolean: - ReadValueSafe(out byte boolVal); - value = Enum.ToObject(type, boolVal != 0); - return; - case TypeCode.Char: - ReadValueSafe(out char charVal); - value = Enum.ToObject(type, charVal); - return; - case TypeCode.SByte: - ReadValueSafe(out sbyte sbyteVal); - value = Enum.ToObject(type, sbyteVal); - return; - case TypeCode.Byte: - ReadValueSafe(out byte byteVal); - value = Enum.ToObject(type, byteVal); - return; - case TypeCode.Int16: - ReadValueSafe(out short shortVal); - value = Enum.ToObject(type, shortVal); - return; - case TypeCode.UInt16: - ReadValueSafe(out ushort ushortVal); - value = Enum.ToObject(type, ushortVal); - return; - case TypeCode.Int32: - ReadValueSafe(out int intVal); - value = Enum.ToObject(type, intVal); - return; - case TypeCode.UInt32: - ReadValueSafe(out uint uintVal); - value = Enum.ToObject(type, uintVal); - return; - case TypeCode.Int64: - ReadValueSafe(out long longVal); - value = Enum.ToObject(type, longVal); - return; - case TypeCode.UInt64: - ReadValueSafe(out ulong ulongVal); - value = Enum.ToObject(type, ulongVal); - return; - } - } - - if (type == typeof(GameObject)) - { - ReadValueSafe(out GameObject go); - value = go; - return; - } - - if (type == typeof(NetworkObject)) - { - ReadValueSafe(out NetworkObject no); - value = no; - return; - } - - if (typeof(NetworkBehaviour).IsAssignableFrom(type)) - { - ReadValueSafe(out NetworkBehaviour nb); - value = nb; - return; - } - /*if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - }*/ - - throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Read an INetworkSerializable - /// - /// INetworkSerializable instance - /// - /// - public void ReadNetworkSerializable(out T value) where T : INetworkSerializable - { - throw new NotImplementedException(); - } - - /// - /// Read a GameObject - /// - /// value to read - public void ReadValue(out GameObject value) - { - ReadValue(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.gameObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a GameObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. - /// - /// value to read - public void ReadValueSafe(out GameObject value) - { - ReadValueSafe(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.gameObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkObject - /// - /// value to read - public void ReadValue(out NetworkObject value) - { - ReadValue(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. - /// - /// value to read - public void ReadValueSafe(out NetworkObject value) - { - ReadValueSafe(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkBehaviour - /// - /// value to read - public void ReadValue(out NetworkBehaviour value) - { - ReadValue(out ulong networkObjectId); - ReadValue(out ushort networkBehaviourId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkBehaviour - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. - /// - /// value to read - public void ReadValueSafe(out NetworkBehaviour value) - { - ReadValueSafe(out ulong networkObjectId); - ReadValueSafe(out ushort networkBehaviourId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - /// /// Reads a string /// NOTE: ALLOCATES @@ -656,7 +380,7 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) /// NOTE: ALLOCATES /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Stores the read string /// Whether or not to use one byte per character. This will only allow ASCII @@ -670,14 +394,14 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) } #endif - if (!VerifyCanReadInternal(sizeof(uint))) + if (!TryBeginReadInternal(sizeof(uint))) { throw new OverflowException("Reading past the end of the buffer"); } ReadValue(out uint length); - if (!VerifyCanReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) + if (!TryBeginReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) { throw new OverflowException("Reading past the end of the buffer"); } @@ -723,7 +447,7 @@ public unsafe void ReadValue(out T[] array) where T : unmanaged /// NOTE: ALLOCATES /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Stores the read array [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -737,13 +461,13 @@ public unsafe void ReadValueSafe(out T[] array) where T : unmanaged } #endif - if (!VerifyCanReadInternal(sizeof(int))) + if (!TryBeginReadInternal(sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); } ReadValue(out int sizeInTs); int sizeInBytes = sizeInTs * sizeof(T); - if (!VerifyCanReadInternal(sizeInBytes)) + if (!TryBeginReadInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -775,7 +499,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB } if (PositionInternal + bytesToRead > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif @@ -803,7 +527,7 @@ public unsafe void ReadByte(out byte value) } if (PositionInternal + 1 > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif value = BufferPointer[PositionInternal++]; @@ -813,7 +537,7 @@ public unsafe void ReadByte(out byte value) /// Read a byte to the stream. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Stores the read value [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -827,7 +551,7 @@ public unsafe void ReadByteSafe(out byte value) } #endif - if (!VerifyCanReadInternal(1)) + if (!TryBeginReadInternal(1)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -851,7 +575,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); @@ -862,7 +586,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) /// Read multiple bytes to the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Pointer to the destination buffer /// Number of bytes to read - MUST BE <= BUFFER SIZE @@ -878,7 +602,7 @@ public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) } #endif - if (!VerifyCanReadInternal(size)) + if (!TryBeginReadInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -905,7 +629,7 @@ public unsafe void ReadBytes(ref byte[] value, int size, int offset = 0) /// Read multiple bytes from the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Pointer to the destination buffer /// Number of bytes to read - MUST BE <= BUFFER SIZE @@ -938,7 +662,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged } if (PositionInternal + len > AllowedReadMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif @@ -954,7 +678,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged /// It will be copied from the buffer exactly as it existed in memory on the writing end. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// The read value /// Any unmanaged type @@ -971,7 +695,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged } #endif - if (!VerifyCanReadInternal(len)) + if (!TryBeginReadInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs new file mode 100644 index 0000000000..21d7daf745 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -0,0 +1,287 @@ + +using System; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class FastBufferReaderExtensions + { + + /// + /// Reads a boxed object in a standard format + /// Named differently from other ReadValue methods to avoid accidental boxing + /// + /// The object to read + /// The type to be read + /// + /// If true, reads a byte indicating whether or not the object is null. + /// Should match the way the object was written. + /// + public static void ReadObject(this ref FastBufferReader reader, out object value, Type type, bool isNullable = false) + { + if (isNullable || type.IsNullable()) + { + reader.ReadValueSafe(out bool isNull); + + if (isNull) + { + value = null; + return; + } + } + + var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); + if (hasDeserializer) + { + deserializer(ref reader, out value); + return; + } + + if (type.IsArray && type.HasElementType) + { + reader.ReadValueSafe(out int length); + + var arr = Array.CreateInstance(type.GetElementType(), length); + + for (int i = 0; i < length; i++) + { + reader.ReadObject(out object item, type.GetElementType()); + arr.SetValue(item, i); + } + + value = arr; + return; + } + + if (type.IsEnum) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + reader.ReadValueSafe(out byte boolVal); + value = Enum.ToObject(type, boolVal != 0); + return; + case TypeCode.Char: + reader.ReadValueSafe(out char charVal); + value = Enum.ToObject(type, charVal); + return; + case TypeCode.SByte: + reader.ReadValueSafe(out sbyte sbyteVal); + value = Enum.ToObject(type, sbyteVal); + return; + case TypeCode.Byte: + reader.ReadValueSafe(out byte byteVal); + value = Enum.ToObject(type, byteVal); + return; + case TypeCode.Int16: + reader.ReadValueSafe(out short shortVal); + value = Enum.ToObject(type, shortVal); + return; + case TypeCode.UInt16: + reader.ReadValueSafe(out ushort ushortVal); + value = Enum.ToObject(type, ushortVal); + return; + case TypeCode.Int32: + reader.ReadValueSafe(out int intVal); + value = Enum.ToObject(type, intVal); + return; + case TypeCode.UInt32: + reader.ReadValueSafe(out uint uintVal); + value = Enum.ToObject(type, uintVal); + return; + case TypeCode.Int64: + reader.ReadValueSafe(out long longVal); + value = Enum.ToObject(type, longVal); + return; + case TypeCode.UInt64: + reader.ReadValueSafe(out ulong ulongVal); + value = Enum.ToObject(type, ulongVal); + return; + } + } + + if (type == typeof(GameObject)) + { + reader.ReadValueSafe(out GameObject go); + value = go; + return; + } + + if (type == typeof(NetworkObject)) + { + reader.ReadValueSafe(out NetworkObject no); + value = no; + return; + } + + if (typeof(NetworkBehaviour).IsAssignableFrom(type)) + { + reader.ReadValueSafe(out NetworkBehaviour nb); + value = nb; + return; + } + /*if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + }*/ + + throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + /// + /// Read an INetworkSerializable + /// + /// INetworkSerializable instance + /// + /// + public static void ReadNetworkSerializable(this ref FastBufferReader reader, out T value) where T : INetworkSerializable + { + throw new NotImplementedException(); + } + + /// + /// Read a GameObject + /// + /// value to read + public static void ReadValue(this ref FastBufferReader reader, out GameObject value) + { + reader.ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject value) + { + reader.ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkObject + /// + /// value to read + public static void ReadValue(this ref FastBufferReader reader, out NetworkObject value) + { + reader.ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject value) + { + reader.ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkBehaviour + /// + /// value to read + public static void ReadValue(this ref FastBufferReader reader, out NetworkBehaviour value) + { + reader.ReadValue(out ulong networkObjectId); + reader.ReadValue(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour value) + { + reader.ReadValueSafe(out ulong networkObjectId); + reader.ReadValueSafe(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta new file mode 100644 index 0000000000..d00798303c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4dc2c9158967ec847895c0d9653283fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 91ac86ba91..22fea00ced 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -177,19 +177,19 @@ internal unsafe void Grow() /// /// Allows faster serialization by batching bounds checking. /// When you know you will be writing multiple fields back-to-back and you know the total size, - /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// you can call TryBeginWrite() once on the total size, and then follow it with calls to /// WriteValue() instead of WriteValueSafe() for faster serialization. /// /// Unsafe write operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following + /// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following /// operations in release builds. /// /// Amount of bytes to write /// True if the write is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanWrite(int bytes) + public bool TryBeginWrite(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -218,19 +218,20 @@ public bool VerifyCanWrite(int bytes) /// /// Allows faster serialization by batching bounds checking. /// When you know you will be writing multiple fields back-to-back and you know the total size, - /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// you can call TryBeginWrite() once on the total size, and then follow it with calls to /// WriteValue() instead of WriteValueSafe() for faster serialization. /// /// Unsafe write operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following - /// operations in release builds. + /// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following + /// operations in release builds. Instead, attempting to write past the marked position in release builds + /// will write to random memory and cause undefined behavior, likely including instability and crashes. /// /// The value you want to write /// True if the write is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged + public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -258,14 +259,14 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } /// - /// Internal version of VerifyCanWrite. - /// Differs from VerifyCanWrite only in that it won't ever move the AllowedWriteMark backward. + /// Internal version of TryBeginWrite. + /// Differs from TryBeginWrite only in that it won't ever move the AllowedWriteMark backward. /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanWriteInternal(int bytes) + public bool TryBeginWriteInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -330,292 +331,6 @@ public unsafe byte[] ToArray() return BufferPointer + PositionInternal; } - /// - /// Writes a boxed object in a standard format - /// Named differently from other WriteValue methods to avoid accidental boxing - /// - /// The object to write - /// - /// If true, an extra byte will be written to indicate whether or not the value is null. - /// Some types will always write this. - /// - public void WriteObject(object value, bool isNullable = false) - { - if (isNullable || value.GetType().IsNullable()) - { - bool isNull = value == null || (value is UnityEngine.Object o && o == null); - - WriteValueSafe(isNull); - - if (isNull) - { - return; - } - } - - var type = value.GetType(); - var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); - if (hasSerializer) - { - serializer(ref this, value); - return; - } - - if (value is Array array) - { - WriteValueSafe(array.Length); - - for (int i = 0; i < array.Length; i++) - { - WriteObject(array.GetValue(i)); - } - - return; - } - - if (value.GetType().IsEnum) - { - switch (Convert.GetTypeCode(value)) - { - case TypeCode.Boolean: - WriteValueSafe((byte)value); - break; - case TypeCode.Char: - WriteValueSafe((char)value); - break; - case TypeCode.SByte: - WriteValueSafe((sbyte)value); - break; - case TypeCode.Byte: - WriteValueSafe((byte)value); - break; - case TypeCode.Int16: - WriteValueSafe((short)value); - break; - case TypeCode.UInt16: - WriteValueSafe((ushort)value); - break; - case TypeCode.Int32: - WriteValueSafe((int)value); - break; - case TypeCode.UInt32: - WriteValueSafe((uint)value); - break; - case TypeCode.Int64: - WriteValueSafe((long)value); - break; - case TypeCode.UInt64: - WriteValueSafe((ulong)value); - break; - } - return; - } - if (value is GameObject) - { - WriteValueSafe((GameObject)value); - return; - } - if (value is NetworkObject) - { - WriteValueSafe((NetworkObject)value); - return; - } - if (value is NetworkBehaviour) - { - WriteValueSafe((NetworkBehaviour)value); - return; - } - if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - } - - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Write an INetworkSerializable - /// - /// The value to write - /// - public void WriteNetworkSerializable(in T value) where T : INetworkSerializable - { - // TODO - } - - /// - /// Get the required amount of space to write a GameObject - /// - /// - /// - public static int GetWriteSize(GameObject value) - { - return sizeof(ulong); - } - - /// - /// Get the required amount of space to write a GameObject - /// - /// - public static int GetGameObjectWriteSize() - { - return sizeof(ulong); - } - - /// - /// Write a GameObject - /// - /// The value to write - public void WriteValue(GameObject value) - { - var networkObject = (value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); - } - - WriteValue(networkObject.NetworkObjectId); - } - - /// - /// Write a GameObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. - /// - /// The value to write - public void WriteValueSafe(GameObject value) - { - var networkObject = (value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); - } - - WriteValueSafe(networkObject.NetworkObjectId); - } - - /// - /// Get the required size to write a NetworkObject - /// - /// - /// - public static int GetWriteSize(NetworkObject value) - { - return sizeof(ulong); - } - - /// - /// Get the required size to write a NetworkObject - /// - /// - public static int GetNetworkObjectWriteSize() - { - return sizeof(ulong); - } - - - /// - /// Write a NetworkObject - /// - /// The value to write - public void WriteValue(in NetworkObject value) - { - if (!value.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); - } - - WriteValue(value.NetworkObjectId); - } - - /// - /// Write a NetworkObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. - /// - /// The value to write - public void WriteValueSafe(NetworkObject value) - { - if (!value.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); - } - WriteValueSafe(value.NetworkObjectId); - } - - /// - /// Get the required size to write a NetworkBehaviour - /// - /// - /// - public static int GetWriteSize(NetworkBehaviour value) - { - return sizeof(ulong) + sizeof(ushort); - } - - - /// - /// Get the required size to write a NetworkBehaviour - /// - /// - public static int GetNetworkBehaviourWriteSize() - { - return sizeof(ulong) + sizeof(ushort); - } - - - /// - /// Write a NetworkBehaviour - /// - /// The value to write - public void WriteValue(NetworkBehaviour value) - { - if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); - } - - WriteValue(value.NetworkObjectId); - WriteValue(value.NetworkBehaviourId); - } - - /// - /// Write a NetworkBehaviour - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. - /// - /// The value to write - /// - /// - public void WriteValueSafe(NetworkBehaviour value) - { - if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); - } - - if (!VerifyCanWriteInternal(sizeof(ulong) + sizeof(ushort))) - { - throw new OverflowException("Writing past the end of the buffer"); - } - WriteValue(value.NetworkObjectId); - WriteValue(value.NetworkBehaviourId); - } - /// /// Get the required size to write a string /// @@ -657,7 +372,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) /// Writes a string /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// The string to write /// Whether or not to use one byte per character. This will only allow ASCII @@ -673,7 +388,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) int sizeInBytes = GetWriteSize(s, oneByteChars); - if (!VerifyCanWriteInternal(sizeInBytes)) + if (!TryBeginWriteInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -735,7 +450,7 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher /// Writes an unmanaged array /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// The array to write /// The amount of elements to write @@ -754,7 +469,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); - if (!VerifyCanWriteInternal(sizeInBytes + sizeof(int))) + if (!TryBeginWriteInternal(sizeInBytes + sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); } @@ -786,7 +501,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt } if (PositionInternal + bytesToWrite > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif @@ -812,7 +527,7 @@ public unsafe void WriteByte(byte value) } if (PositionInternal + 1 > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif BufferPointer[PositionInternal++] = value; @@ -822,7 +537,7 @@ public unsafe void WriteByte(byte value) /// Write a byte to the stream. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -836,7 +551,7 @@ public unsafe void WriteByteSafe(byte value) } #endif - if (!VerifyCanWriteInternal(1)) + if (!TryBeginWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -860,7 +575,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); @@ -871,7 +586,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) /// Write multiple bytes to the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// Value to write /// Number of bytes to write @@ -887,7 +602,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) } #endif - if (!VerifyCanWriteInternal(size)) + if (!TryBeginWriteInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -914,7 +629,7 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) /// Write multiple bytes to the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// Value to write /// Number of bytes to write @@ -994,7 +709,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged } if (PositionInternal + len > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif @@ -1010,7 +725,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged /// It will be copied into the buffer exactly as it exists in memory. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// The value to copy /// Any unmanaged type @@ -1027,7 +742,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged } #endif - if (!VerifyCanWriteInternal(len)) + if (!TryBeginWriteInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs new file mode 100644 index 0000000000..3fea8ee5c0 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -0,0 +1,298 @@ + +using System; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class FastBufferWriterExtensions + { + + /// + /// Writes a boxed object in a standard format + /// Named differently from other WriteValue methods to avoid accidental boxing + /// + /// The object to write + /// + /// If true, an extra byte will be written to indicate whether or not the value is null. + /// Some types will always write this. + /// + public static void WriteObject(this ref FastBufferWriter writer, object value, bool isNullable = false) + { + if (isNullable || value.GetType().IsNullable()) + { + bool isNull = value == null || (value is UnityEngine.Object o && o == null); + + writer.WriteValueSafe(isNull); + + if (isNull) + { + return; + } + } + + var type = value.GetType(); + var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); + if (hasSerializer) + { + serializer(ref writer, value); + return; + } + + if (value is Array array) + { + writer.WriteValueSafe(array.Length); + + for (int i = 0; i < array.Length; i++) + { + writer.WriteObject(array.GetValue(i)); + } + + return; + } + + if (value.GetType().IsEnum) + { + switch (Convert.GetTypeCode(value)) + { + case TypeCode.Boolean: + writer.WriteValueSafe((byte)value); + break; + case TypeCode.Char: + writer.WriteValueSafe((char)value); + break; + case TypeCode.SByte: + writer.WriteValueSafe((sbyte)value); + break; + case TypeCode.Byte: + writer.WriteValueSafe((byte)value); + break; + case TypeCode.Int16: + writer.WriteValueSafe((short)value); + break; + case TypeCode.UInt16: + writer.WriteValueSafe((ushort)value); + break; + case TypeCode.Int32: + writer.WriteValueSafe((int)value); + break; + case TypeCode.UInt32: + writer.WriteValueSafe((uint)value); + break; + case TypeCode.Int64: + writer.WriteValueSafe((long)value); + break; + case TypeCode.UInt64: + writer.WriteValueSafe((ulong)value); + break; + } + return; + } + if (value is GameObject) + { + writer.WriteValueSafe((GameObject)value); + return; + } + if (value is NetworkObject) + { + writer.WriteValueSafe((NetworkObject)value); + return; + } + if (value is NetworkBehaviour) + { + writer.WriteValueSafe((NetworkBehaviour)value); + return; + } + if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + } + + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + /// + /// Write an INetworkSerializable + /// + /// The value to write + /// + public static void WriteNetworkSerializable(this ref FastBufferWriter writer, in T value) where T : INetworkSerializable + { + // TODO + } + + /// + /// Get the required amount of space to write a GameObject + /// + /// + /// + public static int GetWriteSize(GameObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required amount of space to write a GameObject + /// + /// + public static int GetGameObjectWriteSize() + { + return sizeof(ulong); + } + + /// + /// Write a GameObject + /// + /// The value to write + public static void WriteValue(this ref FastBufferWriter writer, GameObject value) + { + value.TryGetComponent(out var networkObject); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); + } + + writer.WriteValue(networkObject.NetworkObjectId); + } + + /// + /// Write a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject value) + { + value.TryGetComponent(out var networkObject); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); + } + + writer.WriteValueSafe(networkObject.NetworkObjectId); + } + + /// + /// Get the required size to write a NetworkObject + /// + /// + /// + public static int GetWriteSize(NetworkObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required size to write a NetworkObject + /// + /// + public static int GetNetworkObjectWriteSize() + { + return sizeof(ulong); + } + + + /// + /// Write a NetworkObject + /// + /// The value to write + public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + + writer.WriteValue(value.NetworkObjectId); + } + + /// + /// Write a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + writer.WriteValueSafe(value.NetworkObjectId); + } + + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + /// + public static int GetWriteSize(NetworkBehaviour value) + { + return sizeof(ulong) + sizeof(ushort); + } + + + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + public static int GetNetworkBehaviourWriteSize() + { + return sizeof(ulong) + sizeof(ushort); + } + + + /// + /// Write a NetworkBehaviour + /// + /// The value to write + public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); + } + + writer.WriteValue(value.NetworkObjectId); + writer.WriteValue(value.NetworkBehaviourId); + } + + /// + /// Write a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// + /// + public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); + } + + if (!writer.TryBeginWriteInternal(sizeof(ulong) + sizeof(ushort))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + writer.WriteValue(value.NetworkObjectId); + writer.WriteValue(value.NetworkBehaviourId); + } + + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta new file mode 100644 index 0000000000..b96c1f3b21 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a9dd964797964b4a99e03706516a8a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index 9d04ce6120..7d181d3f11 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -6,34 +6,34 @@ namespace Unity.Netcode.Serialization { public interface IBufferSerializerImplementation { - public bool IsReader { get; } - public bool IsWriter { get; } + bool IsReader { get; } + bool IsWriter { get; } - public ref FastBufferReader GetFastBufferReader(); - public ref FastBufferWriter GetFastBufferWriter(); + ref FastBufferReader GetFastBufferReader(); + ref FastBufferWriter GetFastBufferWriter(); - public void SerializeValue(ref object value, Type type, bool isNullable = false); - public void SerializeValue(ref INetworkSerializable value); - public void SerializeValue(ref GameObject value); - public void SerializeValue(ref NetworkObject value); - public void SerializeValue(ref NetworkBehaviour value); - public void SerializeValue(ref string s, bool oneByteChars = false); - public void SerializeValue(ref T[] array) where T : unmanaged; - public void SerializeValue(ref byte value); - public void SerializeValue(ref T value) where T : unmanaged; + void SerializeValue(ref object value, Type type, bool isNullable = false); + void SerializeValue(ref INetworkSerializable value); + void SerializeValue(ref GameObject value); + void SerializeValue(ref NetworkObject value); + void SerializeValue(ref NetworkBehaviour value); + void SerializeValue(ref string s, bool oneByteChars = false); + void SerializeValue(ref T[] array) where T : unmanaged; + void SerializeValue(ref byte value); + void SerializeValue(ref T value) where T : unmanaged; // Has to have a different name to avoid conflicting with "where T: unmananged" // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables // So this is provided as an alternative to avoid boxing allocations. - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; + void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; - public bool PreCheck(int amount); - public void SerializeValuePreChecked(ref GameObject value); - public void SerializeValuePreChecked(ref NetworkObject value); - public void SerializeValuePreChecked(ref NetworkBehaviour value); - public void SerializeValuePreChecked(ref string s, bool oneByteChars = false); - public void SerializeValuePreChecked(ref T[] array) where T : unmanaged; - public void SerializeValuePreChecked(ref byte value); - public void SerializeValuePreChecked(ref T value) where T : unmanaged; + bool PreCheck(int amount); + void SerializeValuePreChecked(ref GameObject value); + void SerializeValuePreChecked(ref NetworkObject value); + void SerializeValuePreChecked(ref NetworkBehaviour value); + void SerializeValuePreChecked(ref string s, bool oneByteChars = false); + void SerializeValuePreChecked(ref T[] array) where T : unmanaged; + void SerializeValuePreChecked(ref byte value); + void SerializeValuePreChecked(ref T value) where T : unmanaged; } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs new file mode 100644 index 0000000000..e2d22d75b3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -0,0 +1,669 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Multiplayer.Netcode; +using Unity.Netcode.EditorTests; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode +{ + public abstract class BaseFastBufferReaderWriterTest + { + + #region Test Types + protected enum ByteEnum : byte + { + A, + B, + C + }; + protected enum SByteEnum : sbyte + { + A, + B, + C + }; + protected enum ShortEnum : short + { + A, + B, + C + }; + protected enum UShortEnum : ushort + { + A, + B, + C + }; + protected enum IntEnum : int + { + A, + B, + C + }; + protected enum UIntEnum : uint + { + A, + B, + C + }; + protected enum LongEnum : long + { + A, + B, + C + }; + protected enum ULongEnum : ulong + { + A, + B, + C + }; + + protected struct TestStruct + { + public byte A; + public short B; + public ushort C; + public int D; + public uint E; + public long F; + public ulong G; + public bool H; + public char I; + public float J; + public double K; + } + + public enum WriteType + { + WriteDirect, + WriteSafe, + WriteAsObject + } + #endregion + + + protected abstract void RunTypeTest(T valueToTest) where T : unmanaged; + + protected abstract void RunTypeTestSafe(T valueToTest) where T : unmanaged; + + protected abstract void RunObjectTypeTest(T valueToTest) where T : unmanaged; + + protected abstract void RunTypeArrayTest(T[] valueToTest) where T : unmanaged; + + protected abstract void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged; + + protected abstract void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged; + + #region Helpers + protected TestStruct GetTestStruct() + { + var random = new Random(); + + var testStruct = new TestStruct + { + A = (byte)random.Next(), + B = (short)random.Next(), + C = (ushort)random.Next(), + D = (int)random.Next(), + E = (uint)random.Next(), + F = ((long)random.Next() << 32) + random.Next(), + G = ((ulong)random.Next() << 32) + (ulong)random.Next(), + H = true, + I = '\u263a', + J = (float)random.NextDouble(), + K = random.NextDouble(), + }; + + return testStruct; + } + + protected delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + protected void RunGameObjectTest(GameObjectTestDelegate testCode) + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkManager.SetSingleton(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + UnityEngine.Object.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + #endregion + + public void BaseTypeTest(Type testType, WriteType writeType) + { + var random = new Random(); + + void RunTypeTestLocal(T val, WriteType wt) where T : unmanaged + { + switch (wt) + { + case WriteType.WriteDirect: + RunTypeTest(val); + break; + case WriteType.WriteSafe: + RunTypeTestSafe(val); + break; + default: + RunObjectTypeTest(val); + break; + } + } + + if (testType == typeof(byte)) + { + RunTypeTestLocal((byte)random.Next(), writeType); + } + else if (testType == typeof(sbyte)) + { + RunTypeTestLocal((sbyte)random.Next(), writeType); + } + else if (testType == typeof(short)) + { + RunTypeTestLocal((short)random.Next(), writeType); + } + else if (testType == typeof(ushort)) + { + RunTypeTestLocal((ushort)random.Next(), writeType); + } + else if (testType == typeof(int)) + { + RunTypeTestLocal((int)random.Next(), writeType); + } + else if (testType == typeof(uint)) + { + RunTypeTestLocal((uint)random.Next(), writeType); + } + else if (testType == typeof(long)) + { + RunTypeTestLocal(((long)random.Next() << 32) + random.Next(), writeType); + } + else if (testType == typeof(ulong)) + { + RunTypeTestLocal(((ulong)random.Next() << 32) + (ulong)random.Next(), writeType); + } + else if (testType == typeof(bool)) + { + RunTypeTestLocal(true, writeType); + } + else if (testType == typeof(char)) + { + RunTypeTestLocal('a', writeType); + RunTypeTestLocal('\u263a', writeType); + } + else if (testType == typeof(float)) + { + RunTypeTestLocal((float)random.NextDouble(), writeType); + } + else if (testType == typeof(double)) + { + RunTypeTestLocal(random.NextDouble(), writeType); + } + else if (testType == typeof(ByteEnum)) + { + RunTypeTestLocal(ByteEnum.C, writeType); + } + else if (testType == typeof(SByteEnum)) + { + RunTypeTestLocal(SByteEnum.C, writeType); + } + else if (testType == typeof(ShortEnum)) + { + RunTypeTestLocal(ShortEnum.C, writeType); + } + else if (testType == typeof(UShortEnum)) + { + RunTypeTestLocal(UShortEnum.C, writeType); + } + else if (testType == typeof(IntEnum)) + { + RunTypeTestLocal(IntEnum.C, writeType); + } + else if (testType == typeof(UIntEnum)) + { + RunTypeTestLocal(UIntEnum.C, writeType); + } + else if (testType == typeof(LongEnum)) + { + RunTypeTestLocal(LongEnum.C, writeType); + } + else if (testType == typeof(ULongEnum)) + { + RunTypeTestLocal(ULongEnum.C, writeType); + } + else if (testType == typeof(Vector2)) + { + RunTypeTestLocal(new Vector2((float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Vector3)) + { + RunTypeTestLocal(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Vector4)) + { + RunTypeTestLocal(new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Quaternion)) + { + RunTypeTestLocal(new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Color)) + { + RunTypeTestLocal(new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Color32)) + { + RunTypeTestLocal(new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()), writeType); + } + else if (testType == typeof(Ray)) + { + RunTypeTestLocal(new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())), writeType); + } + else if (testType == typeof(Ray2D)) + { + RunTypeTestLocal(new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())), writeType); + } + else if (testType == typeof(TestStruct)) + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct)obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + RunTypeTestLocal(GetTestStruct(), writeType); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + public void BaseArrayTypeTest(Type testType, WriteType writeType) + { + var random = new Random(); + void RunTypeTestLocal(T[] val, WriteType wt) where T : unmanaged + { + switch (wt) + { + case WriteType.WriteDirect: + RunTypeArrayTest(val); + break; + case WriteType.WriteSafe: + RunTypeArrayTestSafe(val); + break; + default: + RunObjectTypeArrayTest(val); + break; + } + } + + if (testType == typeof(byte)) + { + RunTypeTestLocal(new[]{ + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next() + }, writeType); + } + else if (testType == typeof(sbyte)) + { + RunTypeTestLocal(new[]{ + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next() + }, writeType); + } + else if (testType == typeof(short)) + { + RunTypeTestLocal(new[]{ + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next() + }, writeType); + } + else if (testType == typeof(ushort)) + { + RunTypeTestLocal(new[]{ + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next() + }, writeType); + } + else if (testType == typeof(int)) + { + RunTypeTestLocal(new[]{ + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next() + }, writeType); + } + else if (testType == typeof(uint)) + { + RunTypeTestLocal(new[]{ + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next() + }, writeType); + } + else if (testType == typeof(long)) + { + RunTypeTestLocal(new[]{ + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next() + }, writeType); + } + else if (testType == typeof(ulong)) + { + RunTypeTestLocal(new[]{ + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next() + }, writeType); + } + else if (testType == typeof(bool)) + { + RunTypeTestLocal(new[]{ + true, + false, + true, + true, + false, + false, + true, + false, + true + }, writeType); + } + else if (testType == typeof(char)) + { + RunTypeTestLocal(new[]{ + 'a', + '\u263a' + }, writeType); + } + else if (testType == typeof(float)) + { + RunTypeTestLocal(new[]{ + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble() + }, writeType); + } + else if (testType == typeof(double)) + { + RunTypeTestLocal(new[]{ + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble() + }, writeType); + } + else if (testType == typeof(ByteEnum)) + { + RunTypeTestLocal(new[]{ + ByteEnum.C, + ByteEnum.A, + ByteEnum.B + }, writeType); + } + else if (testType == typeof(SByteEnum)) + { + RunTypeTestLocal(new[]{ + SByteEnum.C, + SByteEnum.A, + SByteEnum.B + }, writeType); + } + else if (testType == typeof(ShortEnum)) + { + RunTypeTestLocal(new[]{ + ShortEnum.C, + ShortEnum.A, + ShortEnum.B + }, writeType); + } + else if (testType == typeof(UShortEnum)) + { + RunTypeTestLocal(new[]{ + UShortEnum.C, + UShortEnum.A, + UShortEnum.B + }, writeType); + } + else if (testType == typeof(IntEnum)) + { + RunTypeTestLocal(new[]{ + IntEnum.C, + IntEnum.A, + IntEnum.B + }, writeType); + } + else if (testType == typeof(UIntEnum)) + { + RunTypeTestLocal(new[]{ + UIntEnum.C, + UIntEnum.A, + UIntEnum.B + }, writeType); + } + else if (testType == typeof(LongEnum)) + { + RunTypeTestLocal(new[]{ + LongEnum.C, + LongEnum.A, + LongEnum.B + }, writeType); + } + else if (testType == typeof(ULongEnum)) + { + RunTypeTestLocal(new[]{ + ULongEnum.C, + ULongEnum.A, + ULongEnum.B + }, writeType); + } + else if (testType == typeof(Vector2)) + { + RunTypeTestLocal(new[]{ + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Vector3)) + { + RunTypeTestLocal(new[]{ + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Vector4)) + { + RunTypeTestLocal(new[]{ + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Quaternion)) + { + RunTypeTestLocal(new[]{ + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Color)) + { + RunTypeTestLocal(new[]{ + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Color32)) + { + RunTypeTestLocal(new[]{ + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + }, writeType); + } + else if (testType == typeof(Ray)) + { + RunTypeTestLocal(new[]{ + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + }, writeType); + } + else if (testType == typeof(Ray2D)) + { + RunTypeTestLocal(new[]{ + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + }, writeType); + } + else if (testType == typeof(TestStruct)) + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct)obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + RunTypeTestLocal(new[] { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }, writeType); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta new file mode 100644 index 0000000000..f0b683d274 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 573b1f36caed496a9c6e0eaa788d0c29 +timeCreated: 1629917174 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 8584391af1..96d68f8c67 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -6,57 +6,66 @@ namespace Unity.Netcode.EditorTests public class BitCounterTests { [Test] - public void TestBitCounter64Bits() + public void WhenCountingUsedBitsIn64BitValue_ResultMatchesHighBitSetPlusOne([Range(0, 63)] int highBit) { - ulong value = 0; - // 0 is a special case. All values are considered at least 1 bit. - Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); - - for (int i = 0; i < 64; ++i) + if (highBit == 0) + { + ulong value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + } + else { - value = 1UL << i; - Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); + ulong value = 1UL << highBit; + Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value)); } } [Test] - public void TestBitCounter32Bits() + public void WhenCountingUsedBitsIn32BitValue_ResultMatchesHighBitSetPlusOne([Range(0, 31)] int highBit) { - uint value = 0; - // 0 is a special case. All values are considered at least 1 bit. - Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); - - for (int i = 0; i < 32; ++i) + if (highBit == 0) { - value = 1U << i; - Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); + uint value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + } + else + { + uint value = 1U << highBit; + Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value)); } } + [Test] - public void TestByteCounter64Bits() + public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 63)] int highBit) { - ulong value = 0; - // 0 is a special case. All values are considered at least 1 byte. - Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); - - for (int i = 0; i < 64; ++i) + if (highBit == 0) { - value = 1UL << i; - Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); + ulong value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + } + else + { + ulong value = 1UL << highBit; + Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); } } - + [Test] - public void TestByteCounter32Bits() + public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 31)] int highBit) { - uint value = 0; - // 0 is a special case. All values are considered at least 1 byte. - Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); - - for (int i = 0; i < 32; ++i) + if (highBit == 0) + { + uint value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + } + else { - value = 1U << i; - Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); + uint value = 1U << highBit; + Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index a63f84c48d..533c6bf963 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -13,7 +13,7 @@ public void TestReadingOneBit() var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBit(true); @@ -39,7 +39,7 @@ public void TestReadingOneBit() var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(3)); + Assert.IsTrue(reader.TryBeginRead(3)); using (var bitReader = reader.EnterBitwiseContext()) { bool b; @@ -77,7 +77,7 @@ public void TestReadingOneBit() } } [Test] - public unsafe void TestVerifyCanReadBits() + public unsafe void TestTryBeginReadBits() { var nativeArray = new NativeArray(4, Allocator.Temp); var reader = new FastBufferReader(nativeArray, Allocator.Temp); @@ -89,9 +89,9 @@ public unsafe void TestVerifyCanReadBits() using (var bitReader = reader.EnterBitwiseContext()) { - Assert.Throws(() => reader.VerifyCanRead(1)); - Assert.Throws(() => reader.VerifyCanReadValue(1)); - Assert.IsTrue(bitReader.VerifyCanReadBits(1)); + Assert.Throws(() => reader.TryBeginRead(1)); + Assert.Throws(() => reader.TryBeginReadValue(1)); + Assert.IsTrue(bitReader.TryBeginReadBits(1)); bitReader.ReadBit(out bool b); Assert.IsTrue(b); @@ -108,7 +108,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(bitReader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.TryBeginReadBits(3)); bitReader.ReadBit(out b); Assert.IsTrue(b); bitReader.ReadBit(out b); @@ -142,7 +142,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(bitReader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.TryBeginReadBits(3)); try { @@ -156,11 +156,11 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(bitReader.VerifyCanReadBits(4)); + Assert.IsTrue(bitReader.TryBeginReadBits(4)); bitReader.ReadBits(out byteVal, 3); Assert.AreEqual(0b010, byteVal); - Assert.IsTrue(bitReader.VerifyCanReadBits(5)); + Assert.IsTrue(bitReader.TryBeginReadBits(5)); bitReader.ReadBits(out byteVal, 5); Assert.AreEqual(0b10101, byteVal); @@ -168,18 +168,18 @@ public unsafe void TestVerifyCanReadBits() Assert.AreEqual(2, reader.Position); - Assert.IsTrue(reader.VerifyCanRead(1)); + Assert.IsTrue(reader.TryBeginRead(1)); reader.ReadByte(out byte nextByte); Assert.AreEqual(0b11111111, nextByte); - Assert.IsTrue(reader.VerifyCanRead(1)); + Assert.IsTrue(reader.TryBeginRead(1)); reader.ReadByte(out nextByte); Assert.AreEqual(0b00000000, nextByte); - Assert.IsFalse(reader.VerifyCanRead(1)); + Assert.IsFalse(reader.TryBeginRead(1)); using (var bitReader = reader.EnterBitwiseContext()) { - Assert.IsFalse(bitReader.VerifyCanReadBits(1)); + Assert.IsFalse(bitReader.TryBeginReadBits(1)); } } } @@ -190,7 +190,7 @@ public void TestReadingMultipleBits() var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111, 1); @@ -205,7 +205,7 @@ public void TestReadingMultipleBits() var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(3)); + Assert.IsTrue(reader.TryBeginRead(3)); using (var bitReader = reader.EnterBitwiseContext()) { byte b; @@ -237,7 +237,7 @@ public void TestReadingMultipleBitsToLongs() var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111UL, 1); @@ -252,7 +252,7 @@ public void TestReadingMultipleBitsToLongs() var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(3)); + Assert.IsTrue(reader.TryBeginRead(3)); using (var bitReader = reader.EnterBitwiseContext()) { ulong ul; @@ -279,7 +279,7 @@ public void TestReadingMultipleBitsToLongs() } [Test] - public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) + public unsafe void TestReadingMultipleBytesToLongs([Range(1U, 64U)] uint numBits) { ulong value = 0xFFFFFFFFFFFFFFFF; var reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); @@ -296,7 +296,7 @@ public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) ulong readValue; - Assert.IsTrue(reader.VerifyCanRead(sizeof(ulong))); + Assert.IsTrue(reader.TryBeginRead(sizeof(ulong))); using (var bitReader = reader.EnterBitwiseContext()) { bitReader.ReadBits(out readValue, numBits); @@ -306,7 +306,7 @@ public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) } [Test] - public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() + public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled() { var nativeArray = new NativeArray(4, Allocator.Temp); var reader = new FastBufferReader(nativeArray, Allocator.Temp); @@ -344,7 +344,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() Assert.Throws(() => { - Assert.IsTrue(reader.VerifyCanRead(1)); + Assert.IsTrue(reader.TryBeginRead(1)); using (var bitReader = reader.EnterBitwiseContext()) { ulong ul; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index 2459147dd6..612fcb1f2f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -17,7 +17,7 @@ public unsafe void TestWritingOneBit() Assert.AreEqual(0, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBit(true); @@ -51,7 +51,7 @@ public unsafe void TestWritingOneBit() } } [Test] - public unsafe void TestVerifyCanWriteBits() + public unsafe void TestTryBeginWriteBits() { var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) @@ -62,9 +62,9 @@ public unsafe void TestVerifyCanWriteBits() using (var bitWriter = writer.EnterBitwiseContext()) { - Assert.Throws(() => writer.VerifyCanWrite(1)); - Assert.Throws(() => writer.VerifyCanWriteValue(1)); - Assert.IsTrue(bitWriter.VerifyCanWriteBits(1)); + Assert.Throws(() => writer.TryBeginWrite(1)); + Assert.Throws(() => writer.TryBeginWriteValue(1)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(1)); bitWriter.WriteBit(true); Assert.AreEqual(0b1, *asInt); @@ -81,7 +81,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(3)); bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); @@ -114,7 +114,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(3)); try { @@ -128,13 +128,13 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(bitWriter.VerifyCanWriteBits(4)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(4)); bitWriter.WriteBits(0b11111010, 3); Assert.AreEqual(0b00101011, *asInt); - Assert.IsTrue(bitWriter.VerifyCanWriteBits(5)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(5)); bitWriter.WriteBits(0b11110101, 5); Assert.AreEqual(0b1010_10101011, *asInt); @@ -143,18 +143,18 @@ public unsafe void TestVerifyCanWriteBits() Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10101011, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(1)); + Assert.IsTrue(writer.TryBeginWrite(1)); writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(1)); + Assert.IsTrue(writer.TryBeginWrite(1)); writer.WriteByte(0b00000000); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); - Assert.IsFalse(writer.VerifyCanWrite(1)); + Assert.IsFalse(writer.TryBeginWrite(1)); using (var bitWriter = writer.EnterBitwiseContext()) { - Assert.IsFalse(bitWriter.VerifyCanWriteBits(1)); + Assert.IsFalse(bitWriter.TryBeginWriteBits(1)); } } } @@ -169,7 +169,7 @@ public unsafe void TestWritingMultipleBits() Assert.AreEqual(0, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111, 1); @@ -206,7 +206,7 @@ public unsafe void TestWritingMultipleBitsFromLongs() Assert.AreEqual(0, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111UL, 1); @@ -234,7 +234,7 @@ public unsafe void TestWritingMultipleBitsFromLongs() } [Test] - public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) + public unsafe void TestWritingMultipleBytesFromLongs([Range(1U, 64U)] uint numBits) { var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); using (writer) @@ -250,7 +250,7 @@ public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) ulong value = 0xFFFFFFFFFFFFFFFF; - Assert.IsTrue(writer.VerifyCanWrite(sizeof(ulong))); + Assert.IsTrue(writer.TryBeginWrite(sizeof(ulong))); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(value, numBits); @@ -260,7 +260,7 @@ public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) } [Test] - public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() + public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled() { var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) @@ -310,7 +310,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() Assert.Throws(() => { - Assert.IsTrue(writer.VerifyCanWrite(1)); + Assert.IsTrue(writer.TryBeginWrite(1)); using (var bitWriter = writer.EnterBitwiseContext()) { try diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index a1bf9b53c8..aa24d4e030 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -525,7 +525,7 @@ public void TestSerializingGameObjectsPreChecked() // Pass } - Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(obj))); + Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(obj))); serializer.SerializeValuePreChecked(ref obj); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -543,7 +543,7 @@ public void TestSerializingGameObjectsPreChecked() // Pass } - Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); Assert.AreEqual(obj, readValue); @@ -572,7 +572,7 @@ public void TestSerializingNetworkObjectsPreChecked() // Pass } - Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkObject))); + Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(networkObject))); serializer.SerializeValuePreChecked(ref networkObject); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -590,7 +590,7 @@ public void TestSerializingNetworkObjectsPreChecked() // Pass } - Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); Assert.AreEqual(networkObject, readValue); @@ -619,7 +619,7 @@ public void TestSerializingNetworkBehavioursPreChecked() // Pass } - Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkBehaviour))); + Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); serializer.SerializeValuePreChecked(ref networkBehaviour); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -637,7 +637,7 @@ public void TestSerializingNetworkBehavioursPreChecked() // Pass } - Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); Assert.AreEqual(networkBehaviour, readValue); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 2fcc0f4e28..0ece584a51 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -256,7 +256,7 @@ public void TestPacking64BitsUnsigned() using (writer) { - writer.VerifyCanWrite(9); + writer.TryBeginWrite(9); ulong value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -289,7 +289,7 @@ public void TestPacking32BitsUnsigned() using (writer) { - writer.VerifyCanWrite(9); + writer.TryBeginWrite(9); uint value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -322,7 +322,7 @@ public void TestPacking64BitsSigned() using (writer) { - writer.VerifyCanWrite(9); + writer.TryBeginWrite(9); long value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -367,7 +367,7 @@ public void TestPacking32BitsSigned() using (writer) { - writer.VerifyCanWrite(5); + writer.TryBeginWrite(5); int value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -545,7 +545,7 @@ public void TestBitPacking61BitsUnsigned() using (writer) { - writer.VerifyCanWrite(8); + writer.TryBeginWrite(8); ulong value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -585,7 +585,7 @@ public void TestBitPacking60BitsSigned() using (writer) { - writer.VerifyCanWrite(8); + writer.TryBeginWrite(8); long value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -645,7 +645,7 @@ public void TestBitPacking30BitsUnsigned() using (writer) { - writer.VerifyCanWrite(4); + writer.TryBeginWrite(4); uint value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -685,7 +685,7 @@ public void TestBitPacking29BitsSigned() using (writer) { - writer.VerifyCanWrite(4); + writer.TryBeginWrite(4); int value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -743,7 +743,7 @@ public void TestBitPacking15BitsUnsigned() using (writer) { - writer.VerifyCanWrite(2); + writer.TryBeginWrite(2); ushort value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -782,7 +782,7 @@ public void TestBitPacking14BitsSigned() using (writer) { - writer.VerifyCanWrite(2); + writer.TryBeginWrite(2); short value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index e4c78be467..cf0817f507 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -10,85 +10,12 @@ namespace Unity.Netcode.EditorTests { - public class FastBufferReaderTests + public class FastBufferReaderTests : BaseFastBufferReaderWriterTest { - #region Test Types - private enum ByteEnum : byte - { - A, - B, - C - }; - private enum SByteEnum : sbyte - { - A, - B, - C - }; - private enum ShortEnum : short - { - A, - B, - C - }; - private enum UShortEnum : ushort - { - A, - B, - C - }; - private enum IntEnum : int - { - A, - B, - C - }; - private enum UIntEnum : uint - { - A, - B, - C - }; - private enum LongEnum : long - { - A, - B, - C - }; - private enum ULongEnum : ulong - { - A, - B, - C - }; - - private struct TestStruct - { - public byte A; - public short B; - public ushort C; - public int D; - public uint E; - public long F; - public ulong G; - public bool H; - public char I; - public float J; - public double K; - } - - public enum WriteType - { - WriteDirect, - WriteSafe, - WriteAsObject - } - #endregion - #region Common Checks private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { - Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); Assert.AreEqual(writeSize + 1, writer.Position, failMessage); Assert.AreEqual(writeSize + 1, writer.Length, failMessage); @@ -100,7 +27,7 @@ private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string private void VerifyCheckBytes(ref FastBufferReader reader, int checkPosition, string failMessage = "") { reader.Seek(checkPosition); - reader.VerifyCanRead(2); + reader.TryBeginRead(2); reader.ReadByte(out byte value); Assert.AreEqual(0x80, value, failMessage); @@ -131,7 +58,7 @@ private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToT #endregion #region Generic Checks - private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); Assert.AreEqual(sizeof(T), writeSize); @@ -139,7 +66,7 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; @@ -149,13 +76,13 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged using (reader) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriter.GetWriteSize())); reader.ReadValue(out T result); Assert.AreEqual(valueToTest, result); } } } - private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTestSafe(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -179,7 +106,7 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -211,14 +138,14 @@ private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) whe } } - private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); @@ -229,7 +156,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { VerifyPositionAndLength(ref reader, writer.Length); - Assert.IsTrue(reader.VerifyCanRead(writeSize)); + Assert.IsTrue(reader.TryBeginRead(writeSize)); reader.ReadValue(out T[] result); VerifyArrayEquality(valueToTest, result, 0); @@ -238,7 +165,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged } } - private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTestSafe(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -263,7 +190,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag @@ -290,1594 +217,630 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag } #endregion - #region Helpers - private TestStruct GetTestStruct() + #region Tests + [Test] + public void GivenFastBufferWriterContainingValue_WhenReadingUnmanagedType_ValueMatchesWhatWasWritten( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] + Type testType, + [Values] WriteType writeType) { - var random = new Random(); - - var testStruct = new TestStruct - { - A = (byte)random.Next(), - B = (short)random.Next(), - C = (ushort)random.Next(), - D = (int)random.Next(), - E = (uint)random.Next(), - F = ((long)random.Next() << 32) + random.Next(), - G = ((ulong)random.Next() << 32) + (ulong)random.Next(), - H = true, - I = '\u263a', - J = (float)random.NextDouble(), - K = random.NextDouble(), - }; - - return testStruct; + BaseTypeTest(testType, writeType); } - #endregion - #region Tests [Test] - public void TestReadingBasicTypes( + public void GivenFastBufferWriterContainingValue_WhenReadingArrayOfUnmanagedElementType_ValueMatchesWhatWasWritten( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] Type testType, [Values] WriteType writeType) { - var random = new Random(); + BaseArrayTypeTest(testType, writeType); + } + + [TestCase(false, WriteType.WriteDirect)] + [TestCase(false, WriteType.WriteSafe)] + [TestCase(false, WriteType.WriteAsObject)] + [TestCase(true, WriteType.WriteDirect)] + [TestCase(true, WriteType.WriteSafe)] + public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesWhatWasWritten(bool oneByteChars, WriteType writeType) + { + string valueToTest = "Hello, I am a test string!"; - if (testType == typeof(byte)) + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, oneByteChars); + + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { - byte b = (byte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(b); - } - else if (writeType == WriteType.WriteSafe) + switch (writeType) { - RunTypeTestSafe(b); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, oneByteChars); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(valueToTest, oneByteChars); + break; + case WriteType.WriteAsObject: + writer.WriteObject(valueToTest); + serializedValueSize += 1; + break; } - else + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - RunObjectTypeTest(b); + VerifyPositionAndLength(ref reader, writer.Length); + + string result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out result, oneByteChars); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result, oneByteChars); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(string), oneByteChars); + result = (string) resultObj; + break; + } + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); } } - else if (testType == typeof(sbyte)) + } + + + [TestCase(1, 0)] + [TestCase(2, 0)] + [TestCase(3, 0)] + [TestCase(4, 0)] + [TestCase(5, 0)] + [TestCase(6, 0)] + [TestCase(7, 0)] + [TestCase(8, 0)] + + [TestCase(1, 1)] + [TestCase(2, 1)] + [TestCase(3, 1)] + [TestCase(4, 1)] + [TestCase(5, 1)] + [TestCase(6, 1)] + [TestCase(7, 1)] + + [TestCase(1, 2)] + [TestCase(2, 2)] + [TestCase(3, 2)] + [TestCase(4, 2)] + [TestCase(5, 2)] + [TestCase(6, 2)] + + [TestCase(1, 3)] + [TestCase(2, 3)] + [TestCase(3, 3)] + [TestCase(4, 3)] + [TestCase(5, 3)] + + [TestCase(1, 4)] + [TestCase(2, 4)] + [TestCase(3, 4)] + [TestCase(4, 4)] + + [TestCase(1, 5)] + [TestCase(2, 5)] + [TestCase(3, 5)] + + [TestCase(1, 6)] + [TestCase(2, 6)] + + [TestCase(1, 7)] + public void GivenFastBufferWriterContainingValue_WhenReadingPartialValue_ValueMatchesWhatWasWritten(int count, int offset) + { + var random = new Random(); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { - sbyte sb = (sbyte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(sb); - } - else + Assert.IsTrue(writer.TryBeginWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, offset); + + var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; + WriteCheckBytes(ref writer, count, failMessage); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - RunObjectTypeTest(sb); + VerifyPositionAndLength(ref reader, writer.Length, failMessage); + Assert.IsTrue(reader.TryBeginRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + mask <<= (offset * 8); + + reader.ReadPartialValue(out ulong result, count, offset); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); } } - else if (testType == typeof(short)) + } + + + [Test] + public unsafe void GivenFastBufferReaderInitializedFromFastBufferWriterContainingValue_WhenCallingToArray_ReturnedArrayMatchesContentOfWriter() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) { - short s = (short)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(s); - } - else + writer.TryBeginWrite(requiredSize); + writer.WriteValue(testStruct); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - RunObjectTypeTest(s); + var array = reader.ToArray(); + var underlyingArray = writer.GetUnsafePtr(); + for (var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } } } - else if (testType == typeof(ushort)) + } + + + [Test] + public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - ushort us = (ushort)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(us); - } - else - { - RunObjectTypeTest(us); - } + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } - else if (testType == typeof(int)) + } + + [Test] + public void WhenCallingReadBytesWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + byte[] b = {0, 1, 2}; + using (emptyReader) { - int i = (int)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(i); - } - else - { - RunObjectTypeTest(i); - } + Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } - else if (testType == typeof(uint)) + } + + [Test] + public void WhenCallingReadValueWithUnmanagedTypeWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - uint ui = (uint)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ui); - } - else - { - RunObjectTypeTest(ui); - } + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } - else if (testType == typeof(long)) + } + + [Test] + public void WhenCallingReadValueWithByteArrayWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - long l = ((long)random.Next() << 32) + random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(l); - } - else - { - RunObjectTypeTest(l); - } + Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); } - else if (testType == typeof(ulong)) + } + + [Test] + public void WhenCallingReadValueWithStringWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ul); - } - else - { - RunObjectTypeTest(ul); - } + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); } - else if (testType == typeof(bool)) + } + + [Test] + public void WhenCallingReadValueAfterCallingTryBeginWriteWithTooFewBytes_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(true); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(true); - } - else - { - RunObjectTypeTest(true); - } + emptyReader.TryBeginRead(sizeof(int) - 1); + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } - else if (testType == typeof(char)) + } + + [Test] + public void WhenCallingReadBytePastBoundaryMarkedByTryBeginWrite_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - char c = 'a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else - { - RunObjectTypeTest(c); - } + emptyReader.TryBeginRead(sizeof(int) - 1); + emptyReader.ReadByte(out byte b); + emptyReader.ReadByte(out b); + emptyReader.ReadByte(out b); + Assert.Throws(() => { emptyReader.ReadByte(out b); }); + } + } - c = '\u263a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else + [Test] + public void WhenCallingReadByteDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) + { + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(c); + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } } - else if (testType == typeof(float)) + } + + [Test] + public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - float f = (float)random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(f); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(f); + byte[] b = {0, 1, 2}; + Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } } - else if (testType == typeof(double)) + } + + [Test] + public void WhenCallingReadValueWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - double d = random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(d); - } - else if (writeType == WriteType.WriteSafe) + using (var context = emptyReader.EnterBitwiseContext()) { - RunTypeTestSafe(d); - } - else - { - RunObjectTypeTest(d); + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } } - else if (testType == typeof(ByteEnum)) + } + + [Test] + public void WhenCallingReadValueWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - ByteEnum e = ByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); } } - else if (testType == typeof(SByteEnum)) + } + + [Test] + public void WhenCallingReadValueWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - SByteEnum e = SByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); } } - else if (testType == typeof(ShortEnum)) - { - ShortEnum e = ShortEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum e = UShortEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum e = IntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum e = UIntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum e = LongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum e = ULongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color)) - { - var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color32)) - { - var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray2D)) - { - var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), - new Vector2((float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); - } } [Test] - public void TestReadingBasicArrays( - [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), - typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), - typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), - typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] - Type testType, - [Values] WriteType writeType) + public void WhenCallingReadByteSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - var random = new Random(); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - if (testType == typeof(byte)) - { - byte[] b = { - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(sbyte)) - { - sbyte[] sb = { - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(sb); - } - else - { - RunObjectTypeArrayTest(sb); - } - } - else if (testType == typeof(short)) - { - short[] s = { - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(s); - } - else - { - RunObjectTypeArrayTest(s); - } - } - else if (testType == typeof(ushort)) - { - ushort[] us = { - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(us); - } - else - { - RunObjectTypeArrayTest(us); - } - } - else if (testType == typeof(int)) - { - int[] i = { - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(i); - } - else - { - RunObjectTypeArrayTest(i); - } - } - else if (testType == typeof(uint)) - { - uint[] ui = { - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ui); - } - else - { - RunObjectTypeArrayTest(ui); - } - } - else if (testType == typeof(long)) - { - long[] l = { - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(l); - } - else - { - RunObjectTypeArrayTest(l); - } - } - else if (testType == typeof(ulong)) - { - ulong[] ul = { - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ul); - } - else - { - RunObjectTypeArrayTest(ul); - } - } - else if (testType == typeof(bool)) - { - bool[] b = { - true, - false, - true, - true, - false, - false, - true, - false, - true - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(char)) - { - char[] c = { - 'a', - '\u263a', - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(c); - } - else - { - RunObjectTypeArrayTest(c); - } - } - else if (testType == typeof(float)) - { - float[] f = { - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(f); - } - else - { - RunObjectTypeArrayTest(f); - } - } - else if (testType == typeof(double)) - { - double[] d = { - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(d); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(d); - } - else - { - RunObjectTypeArrayTest(d); - } - } - else if (testType == typeof(ByteEnum)) - { - ByteEnum[] e = { - ByteEnum.C, - ByteEnum.A, - ByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(SByteEnum)) - { - SByteEnum[] e = { - SByteEnum.C, - SByteEnum.A, - SByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ShortEnum)) - { - ShortEnum[] e = { - ShortEnum.C, - ShortEnum.A, - ShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum[] e = { - UShortEnum.C, - UShortEnum.A, - UShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum[] e = { - IntEnum.C, - IntEnum.A, - IntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum[] e = { - UIntEnum.C, - UIntEnum.A, - UIntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum[] e = { - LongEnum.C, - LongEnum.A, - LongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum[] e = { - ULongEnum.C, - ULongEnum.A, - ULongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new[] - { - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new[] - { - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new[] - { - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new[] - { - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Color)) + using (emptyReader) { - var v = new[] - { - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) + using (var context = emptyReader.EnterBitwiseContext()) { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); + Assert.Throws(() => { emptyReader.ReadByteSafe(out byte b); }); } } - else if (testType == typeof(Color32)) - { - var v = new[] - { - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - }; + } - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new[] - { - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray2D)) + [Test] + public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - var v = new[] - { - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeArrayTest(v); + byte[] b = {0, 1, 2}; + Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); } } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); - } } [Test] - public void TestReadingStruct() + public void WhenCallingReadValueSafeWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - RunTypeTest(GetTestStruct()); - } - - [Test] - public void TestReadingStructSafe() - { - RunTypeTestSafe(GetTestStruct()); - } + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - [Test] - public void TestReadingStructAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => - { - reader.ReadValueSafe(out TestStruct value); - obj = value; - }; - try - { - RunObjectTypeTest(GetTestStruct()); - } - finally + using (emptyReader) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); - SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + using (var context = emptyReader.EnterBitwiseContext()) + { + Assert.Throws(() => { emptyReader.ReadValueSafe(out int i); }); + } } } [Test] - public void TestReadingStructArray() + public void WhenCallingReadValueSafeWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTest(arr); - } - - [Test] - public void TestReadingStructArraySafe() - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTestSafe(arr); - } + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - [Test] - public void TestReadingStructArrayAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => - { - reader.ReadValueSafe(out TestStruct value); - obj = value; - }; - try - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunObjectTypeArrayTest(arr); - } - finally + using (emptyReader) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); - SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + using (var context = emptyReader.EnterBitwiseContext()) + { + Assert.Throws(() => { emptyReader.ReadValueSafe(out byte[] b); }); + } } } [Test] - public void TestReadingString() + public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) + using (emptyReader) { - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize); + Assert.Throws(() => { emptyReader.ReadValueSafe(out string s); }); } } } - + [Test] - public void TestReadingStringSafe() + public void WhenCallingReadByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(valueToTest); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadValueSafe(out string result); - Assert.AreEqual(valueToTest, result); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - VerifyCheckBytes(ref reader, serializedValueSize); + using (emptyReader) + { + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) + { + context.ReadBit(out bool theBit); } + emptyReader.ReadByte(out byte theByte); } } [Test] - public void TestReadingStringAsObject() + public void WhenCallingReadBytesAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.WriteObject(valueToTest); - - WriteCheckBytes(ref writer, serializedValueSize + 1); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadObject(out object result, typeof(string)); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize + 1); + context.ReadBit(out bool theBit); } + + byte[] theBytes = {0, 1, 2}; + emptyReader.ReadBytes(ref theBytes, 3); } } [Test] - public void TestReadingOneByteString() + public void WhenCallingReadValueWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) + using (emptyReader) { - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest, true); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result, true); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize); + context.ReadBit(out bool theBit); } + emptyReader.ReadValue(out int i); } } [Test] - public void TestReadingOneByteStringSafe() + public void WhenCallingReadValueWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.WriteValueSafe(valueToTest, true); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadValueSafe(out string result, true); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize); + context.ReadBit(out bool theBit); } + emptyReader.ReadValue(out byte[] theBytes); } } [Test] - public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + public void WhenCallingReadValueWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count); - - var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; - WriteCheckBytes(ref writer, count, failMessage); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + using (emptyReader) + { + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) - { - mask = (mask << 8) | 0b11111111; - } - - reader.ReadPartialValue(out ulong result, count); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); + context.ReadBit(out bool theBit); } + emptyReader.ReadValue(out string s); } } - + [Test] - public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) + public void WhenCallingReadByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - using (writer) + using (emptyReader) { - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count, 2); - var failMessage = $"TestReadingPartialValuesWithOffsets failed with value {valueToTest}"; - WriteCheckBytes(ref writer, count, failMessage); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) - { - mask = (mask << 8) | 0b11111111; - } - - mask <<= 16; - - reader.ReadPartialValue(out ulong result, count, 2); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); + context.ReadBit(out bool theBit); } + emptyReader.ReadByteSafe(out byte theByte); } } [Test] - public unsafe void TestToArray() + public void WhenCallingReadBytesSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - var array = reader.ToArray(); - var underlyingArray = writer.GetUnsafePtr(); - for (var i = 0; i < array.Length; ++i) - { - Assert.AreEqual(array[i], underlyingArray[i]); - } + context.ReadBit(out bool theBit); } + + byte[] theBytes = {0, 1, 2}; + emptyReader.ReadBytesSafe(ref theBytes, 3); } } [Test] - public void TestThrowingIfBoundsCheckingSkipped() + public void WhenCallingReadValueSafeWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var writer = new FastBufferWriter(100, Allocator.Temp); var nativeArray = new NativeArray(100, Allocator.Temp); - var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp, 0); - nativeArray.Dispose(); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); using (emptyReader) - using (writer) { - Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); - var bytes = new byte[] { 0, 1, 2 }; - Assert.Throws(() => { emptyReader.ReadBytes(ref bytes, bytes.Length); }); - int i = 1; - Assert.Throws(() => { emptyReader.ReadValue(out int i); }); - Assert.Throws(() => { emptyReader.ReadValue(out bytes); }); - Assert.Throws(() => { emptyReader.ReadValue(out string s); }); - - writer.VerifyCanWrite(sizeof(int) - 1); - writer.WriteByte(1); - writer.WriteByte(2); - writer.WriteByte(3); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - Assert.Throws(() => { reader.ReadValue(out int i); }); - Assert.Throws(() => { reader.ReadValue(out byte b); }); - Assert.IsTrue(reader.VerifyCanRead(3)); - reader.ReadByte(out byte b); - reader.ReadByte(out b); - reader.ReadByte(out b); - Assert.Throws(() => { reader.ReadValue(out byte b); }); + context.ReadBit(out bool theBit); } + emptyReader.ReadValueSafe(out int i); } } [Test] - public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() + public void WhenCallingReadValueSafeWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var writer = new FastBufferWriter(100, Allocator.Temp); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.VerifyCanWrite(100); - var bytes = new byte[] { 0, 1, 2 }; - int i = 1; - writer.WriteByte(1); - writer.WriteBytes(bytes, bytes.Length); - writer.WriteValue(i); - writer.WriteValue(bytes); - writer.WriteValue(""); + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) + { + context.ReadBit(out bool theBit); + } + emptyReader.ReadValueSafe(out byte[] theBytes); + } + } - writer.WriteByteSafe(1); - writer.WriteBytesSafe(bytes, bytes.Length); - writer.WriteValueSafe(i); - writer.WriteValueSafe(bytes); - writer.WriteValueSafe(""); + [Test] + public void WhenCallingReadValueSafeWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + using (emptyReader) + { + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - Assert.IsTrue(reader.VerifyCanRead(writer.Length)); - using (var context = reader.EnterBitwiseContext()) - { - Assert.Throws(() => { reader.ReadByte(out byte b); }); - Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); - Assert.Throws(() => { reader.ReadValue(out i); }); - Assert.Throws(() => { reader.ReadValue(out bytes); }); - Assert.Throws(() => { reader.ReadValue(out string s); }); - - Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); - Assert.Throws(() => - { - reader.ReadBytesSafe(ref bytes, bytes.Length); - }); - Assert.Throws(() => { reader.ReadValueSafe(out i); }); - Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); - Assert.Throws(() => { reader.ReadValueSafe(out string s); }); - } + context.ReadBit(out bool theBit); } + emptyReader.ReadValueSafe(out string s); } } [Test] - public void TestVerifyCanReadIsRelativeToPositionAndNotAllowedReadPosition() + public void WhenCallingTryBeginRead_TheAllowedReadPositionIsMarkedRelativeToCurrentPosition() { var nativeArray = new NativeArray(100, Allocator.Temp); - var reader = new FastBufferReader(nativeArray, Allocator.Temp, 100); - nativeArray.Dispose(); - using (reader) + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - reader.VerifyCanRead(100); - reader.ReadByte(out byte b); - reader.VerifyCanRead(1); - reader.ReadByte(out b); - Assert.Throws(() => { reader.ReadByte(out b); }); + emptyReader.TryBeginRead(100); + emptyReader.ReadByte(out byte b); + emptyReader.TryBeginRead(1); + emptyReader.ReadByte(out b); + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } } [Test] - public void TestSeeking() + public void WhenReadingAfterSeeking_TheNewReadComesFromTheCorrectPosition() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) @@ -1930,85 +893,47 @@ public void TestSeeking() } } - private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, - NetworkObject networkObject); - private void RunGameObjectTest(GameObjectTestDelegate testCode) - { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkManager.SetSingleton(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartServer(); - - try - { - testCode(obj, networkBehaviour, networkObject); - } - finally - { - UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopServer(); - } - } - [Test] - public void TestNetworkBehaviour() + public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObject_TheSameObjectIsRetrieved([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); - - writer.WriteValue(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + switch (writeType) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); - reader.ReadValue(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); + writer.WriteValue(networkObject); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkObject); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkObject); + break; } - } - }); - } - - [Test] - public void TestNetworkObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); - - writer.WriteValue(networkObject); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); - reader.ReadValue(out NetworkObject result); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetNetworkObjectWriteSize())); + NetworkObject result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(networkObject))); + reader.ReadValue(out result); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(NetworkObject)); + result = (NetworkObject) resultObj; + break; + } Assert.AreSame(result, networkObject); } } @@ -2016,113 +941,93 @@ public void TestNetworkObject() } [Test] - public void TestGameObject() + public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_TheSameObjectIsRetrieved([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); - - writer.WriteValue(obj); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + switch (writeType) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); - reader.ReadValue(out GameObject result); - Assert.AreSame(result, obj); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); + writer.WriteValue(obj); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(obj); + break; + case WriteType.WriteAsObject: + writer.WriteObject(obj); + break; } - } - }); - } - - [Test] - public void TestNetworkBehaviourSafe() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - reader.ReadValueSafe(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetGameObjectWriteSize())); + GameObject result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(obj))); + reader.ReadValue(out result); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(GameObject)); + result = (GameObject) resultObj; + break; + } + Assert.AreSame(result, obj); } } }); } [Test] - public void TestNetworkObjectSafe() + public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkBehaviour_TheSameObjectIsRetrieved([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); using (writer) { - writer.WriteValueSafe(networkObject); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + switch (writeType) { - reader.ReadValueSafe(out NetworkObject result); - Assert.AreSame(result, networkObject); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); + writer.WriteValue(networkBehaviour); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkBehaviour); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkBehaviour); + break; } - } - }); - } - - [Test] - public void TestGameObjectSafe() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(obj); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - reader.ReadValueSafe(out GameObject result); - Assert.AreSame(result, obj); - } - } - }); - } - - [Test] - public void TestNetworkBehaviourAsObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); - using (writer) - { - writer.WriteObject(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); - using (reader) - { - reader.ReadObject(out object result, typeof(NetworkBehaviour)); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetNetworkBehaviourWriteSize())); + NetworkBehaviour result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); + reader.ReadValue(out result); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(NetworkBehaviour)); + result = (NetworkBehaviour) resultObj; + break; + } Assert.AreSame(result, networkBehaviour); } } @@ -2130,59 +1035,13 @@ public void TestNetworkBehaviourAsObject() } [Test] - public void TestNetworkObjectAsObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); - using (writer) - { - writer.WriteObject(networkObject); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); - using (reader) - { - reader.ReadObject(out object result, typeof(NetworkObject)); - Assert.AreSame(result, networkObject); - } - } - }); - } - - [Test] - public void TestGameObjectAsObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); - using (writer) - { - writer.WriteObject(obj); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); - using (reader) - { - reader.ReadObject(out object result, typeof(GameObject)); - Assert.AreSame(result, obj); - } - } - }); - } - - [Test] - public void TestVerifyInternalDoesntReduceAllowedWritePoint() + public void WhenCallingTryBeginReadInternal_AllowedReadPositionDoesNotMoveBackward() { var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp), Allocator.Temp); using (reader) { - reader.VerifyCanRead(25); - reader.VerifyCanReadInternal(5); + reader.TryBeginRead(25); + reader.TryBeginReadInternal(5); Assert.AreEqual(reader.AllowedReadMark, 25); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 8fbe632431..37e93eb59d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1,94 +1,19 @@ using System; -using System.Collections.Generic; using NUnit.Framework; -using NUnit.Framework.Internal; using Unity.Collections; using Unity.Multiplayer.Netcode; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode.EditorTests { - public class FastBufferWriterTests + public class FastBufferWriterTests : BaseFastBufferReaderWriterTest { - #region Test Types - private enum ByteEnum : byte - { - A, - B, - C - }; - private enum SByteEnum : sbyte - { - A, - B, - C - }; - private enum ShortEnum : short - { - A, - B, - C - }; - private enum UShortEnum : ushort - { - A, - B, - C - }; - private enum IntEnum : int - { - A, - B, - C - }; - private enum UIntEnum : uint - { - A, - B, - C - }; - private enum LongEnum : long - { - A, - B, - C - }; - private enum ULongEnum : ulong - { - A, - B, - C - }; - - private struct TestStruct - { - public byte A; - public short B; - public ushort C; - public int D; - public uint E; - public long F; - public ulong G; - public bool H; - public char I; - public float J; - public double K; - } - - public enum WriteType - { - WriteDirect, - WriteSafe, - WriteAsObject - } - #endregion #region Common Checks private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { - Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); Assert.AreEqual(writeSize + 1, writer.Position, failMessage); Assert.AreEqual(writeSize + 1, writer.Length, failMessage); @@ -142,7 +67,7 @@ private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, #endregion #region Generic Checks - private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var alternateWriteSize = FastBufferWriter.GetWriteSize(); @@ -154,7 +79,7 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; @@ -163,7 +88,7 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged CommonChecks(ref writer, valueToTest, writeSize, failMessage); } } - private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTestSafe(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -180,7 +105,7 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -196,7 +121,7 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged { int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); @@ -211,7 +136,7 @@ private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offse } } - private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -219,7 +144,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); VerifyPositionAndLength(ref writer, writeSize); @@ -233,7 +158,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged } } - private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTestSafe(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -254,7 +179,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag @@ -278,1574 +203,620 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag } #endregion - #region Helpers - private TestStruct GetTestStruct() - { - var random = new Random(); - var testStruct = new TestStruct - { - A = (byte)random.Next(), - B = (short)random.Next(), - C = (ushort)random.Next(), - D = (int)random.Next(), - E = (uint)random.Next(), - F = ((long)random.Next() << 32) + random.Next(), - G = ((ulong)random.Next() << 32) + (ulong)random.Next(), - H = true, - I = '\u263a', - J = (float)random.NextDouble(), - K = random.NextDouble(), - }; - - return testStruct; + #region Tests + [Test, Description("Tests ")] + public void WhenWritingUnmanagedType_ValueIsWrittenCorrectly( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] + Type testType, + [Values] WriteType writeType) + { + BaseTypeTest(testType, writeType); } - #endregion - #region Tests [Test] - public void TestWritingBasicTypes( + public void WhenWritingArrayOfUnmanagedElementType_ArrayIsWrittenCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] Type testType, [Values] WriteType writeType) { - var random = new Random(); + BaseArrayTypeTest(testType, writeType); + } + + [TestCase(false, WriteType.WriteDirect)] + [TestCase(false, WriteType.WriteSafe)] + [TestCase(false, WriteType.WriteAsObject)] + [TestCase(true, WriteType.WriteDirect)] + [TestCase(true, WriteType.WriteSafe)] + public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, WriteType writeType) + { + string valueToTest = "Hello, I am a test string!"; - if (testType == typeof(byte)) + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, oneByteChars); + + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { - byte b = (byte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(b); - } - else if (writeType == WriteType.WriteSafe) + var offset = 0; + switch (writeType) { - RunTypeTestSafe(b); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, oneByteChars); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(valueToTest, oneByteChars); + break; + case WriteType.WriteAsObject: + writer.WriteObject(valueToTest); + // account for isNull byte + offset = sizeof(byte); + break; + } - else + + VerifyPositionAndLength(ref writer, serializedValueSize+offset); + WriteCheckBytes(ref writer, serializedValueSize+offset); + + int* sizeValue = (int*)(writer.GetUnsafePtr() + offset); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) { - RunObjectTypeTest(b); + if(oneByteChars) + { + byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int) + offset; + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); + } + + } + else + { + char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int) + offset); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } } + + var underlyingArray = writer.ToArray(); + VerifyCheckBytes(underlyingArray, serializedValueSize+offset); } - else if (testType == typeof(sbyte)) + } + + [TestCase(1, 0)] + [TestCase(2, 0)] + [TestCase(3, 0)] + [TestCase(4, 0)] + [TestCase(5, 0)] + [TestCase(6, 0)] + [TestCase(7, 0)] + [TestCase(8, 0)] + + [TestCase(1, 1)] + [TestCase(2, 1)] + [TestCase(3, 1)] + [TestCase(4, 1)] + [TestCase(5, 1)] + [TestCase(6, 1)] + [TestCase(7, 1)] + + [TestCase(1, 2)] + [TestCase(2, 2)] + [TestCase(3, 2)] + [TestCase(4, 2)] + [TestCase(5, 2)] + [TestCase(6, 2)] + + [TestCase(1, 3)] + [TestCase(2, 3)] + [TestCase(3, 3)] + [TestCase(4, 3)] + [TestCase(5, 3)] + + [TestCase(1, 4)] + [TestCase(2, 4)] + [TestCase(3, 4)] + [TestCase(4, 4)] + + [TestCase(1, 5)] + [TestCase(2, 5)] + [TestCase(3, 5)] + + [TestCase(1, 6)] + [TestCase(2, 6)] + + [TestCase(1, 7)] + public unsafe void WhenWritingPartialValueWithCountAndOffset_ValueIsWrittenCorrectly(int count, int offset) + { + var random = new Random(); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { - sbyte sb = (sbyte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(sb); - } - else + + Assert.IsTrue(writer.TryBeginWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, offset); + + var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; + VerifyPositionAndLength(ref writer, count, failMessage); + WriteCheckBytes(ref writer, count, failMessage); + var underlyingArray = writer.ToArray(); + VerifyBytewiseEquality(valueToTest, underlyingArray, offset, 0, count, failMessage); + VerifyCheckBytes(underlyingArray, count, failMessage); + + ulong mask = 0; + for (var i = 0; i < count; ++i) { - RunObjectTypeTest(sb); + mask = (mask << 8) | 0b11111111; } + + ulong* checkValue = (ulong*)writer.GetUnsafePtr(); + Assert.AreEqual((valueToTest >> (offset * 8)) & mask, *checkValue & mask); } - else if (testType == typeof(short)) + } + + [Test] + public void WhenCallingToArray_ReturnedArrayContainsCorrectData() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) { - short s = (short)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(s); - } - else + writer.TryBeginWrite(requiredSize); + writer.WriteValue(testStruct); + var array = writer.ToArray(); + var underlyingArray = writer.ToArray(); + for (var i = 0; i < array.Length; ++i) { - RunObjectTypeTest(s); + Assert.AreEqual(array[i], underlyingArray[i]); } } - else if (testType == typeof(ushort)) + } + + [Test] + public void WhenCallingWriteByteWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ushort us = (ushort)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(us); - } - else - { - RunObjectTypeTest(us); - } + Assert.Throws(() => { writer.WriteByte(1); }); } - else if (testType == typeof(int)) + } + + [Test] + public void WhenCallingWriteBytesWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - int i = (int)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(i); - } - else - { - RunObjectTypeTest(i); - } + var bytes = new byte[] { 0, 1, 2 }; + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } - else if (testType == typeof(uint)) + } + + [Test] + public void WhenCallingWriteValueWithUnmanagedTypeWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - uint ui = (uint)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ui); - } - else - { - RunObjectTypeTest(ui); - } + int i = 1; + Assert.Throws(() => { writer.WriteValue(i); }); } - else if (testType == typeof(long)) + } + + [Test] + public void WhenCallingWriteValueWithByteArrayWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - long l = ((long)random.Next() << 32) + random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(l); - } - else - { - RunObjectTypeTest(l); - } + var bytes = new byte[] { 0, 1, 2 }; + Assert.Throws(() => { writer.WriteValue(bytes); }); } - else if (testType == typeof(ulong)) + } + + [Test] + public void WhenCallingWriteValueWithStringWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ul); - } - else - { - RunObjectTypeTest(ul); - } + Assert.Throws(() => { writer.WriteValue(""); }); } - else if (testType == typeof(bool)) + } + + [Test] + public void WhenCallingWriteValueAfterCallingTryBeginWriteWithTooFewBytes_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(true); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(true); - } - else - { - RunObjectTypeTest(true); - } + int i = 0; + writer.TryBeginWrite(sizeof(int) - 1); + Assert.Throws(() => { writer.WriteValue(i); }); } - else if (testType == typeof(char)) - { - char c = 'a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else - { - RunObjectTypeTest(c); - } + } - c = '\u263a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else - { - RunObjectTypeTest(c); - } + [Test] + public void WhenCallingWriteBytePastBoundaryMarkedByTryBeginWrite_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.TryBeginWrite(sizeof(int) - 1); + writer.WriteByte(1); + writer.WriteByte(2); + writer.WriteByte(3); + Assert.Throws(() => { writer.WriteByte(4); }); } - else if (testType == typeof(float)) + } + + [Test] + public void WhenCallingWriteByteDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - float f = (float)random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(f); - } - else + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(f); + Assert.Throws(() => { writer.WriteByte(1); }); } } - else if (testType == typeof(double)) + } + + [Test] + public void WhenCallingWriteBytesDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - double d = random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(d); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(d); - } - else + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(d); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } - else if (testType == typeof(ByteEnum)) + } + + [Test] + public void WhenCallingWriteValueWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ByteEnum e = ByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + writer.TryBeginWrite(100); + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { writer.WriteValue(i); }); } } - else if (testType == typeof(SByteEnum)) + } + + [Test] + public void WhenCallingWriteValueWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - SByteEnum e = SByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } - else if (testType == typeof(ShortEnum)) + } + + [Test] + public void WhenCallingWriteValueWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ShortEnum e = ShortEnum.C; - if (writeType == WriteType.WriteDirect) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - RunTypeTest(e); + Assert.Throws(() => { writer.WriteValue(""); }); } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum e = UShortEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum e = IntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum e = UIntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum e = LongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum e = ULongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color)) - { - var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color32)) - { - var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray2D)) - { - var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), - new Vector2((float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); } } [Test] - public void TestWritingBasicArrays( - [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), - typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), - typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), - typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] - Type testType, - [Values] WriteType writeType) + public void WhenCallingWriteByteSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - var random = new Random(); + var writer = new FastBufferWriter(100, Allocator.Temp); - if (testType == typeof(byte)) - { - byte[] b = { - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(sbyte)) - { - sbyte[] sb = { - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(sb); - } - else - { - RunObjectTypeArrayTest(sb); - } - } - else if (testType == typeof(short)) - { - short[] s = { - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(s); - } - else - { - RunObjectTypeArrayTest(s); - } - } - else if (testType == typeof(ushort)) - { - ushort[] us = { - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(us); - } - else - { - RunObjectTypeArrayTest(us); - } - } - else if (testType == typeof(int)) - { - int[] i = { - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(i); - } - else - { - RunObjectTypeArrayTest(i); - } - } - else if (testType == typeof(uint)) - { - uint[] ui = { - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ui); - } - else - { - RunObjectTypeArrayTest(ui); - } - } - else if (testType == typeof(long)) - { - long[] l = { - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(l); - } - else - { - RunObjectTypeArrayTest(l); - } - } - else if (testType == typeof(ulong)) - { - ulong[] ul = { - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ul); - } - else - { - RunObjectTypeArrayTest(ul); - } - } - else if (testType == typeof(bool)) - { - bool[] b = { - true, - false, - true, - true, - false, - false, - true, - false, - true - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(char)) - { - char[] c = { - 'a', - '\u263a', - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(c); - } - else - { - RunObjectTypeArrayTest(c); - } - } - else if (testType == typeof(float)) - { - float[] f = { - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(f); - } - else - { - RunObjectTypeArrayTest(f); - } - } - else if (testType == typeof(double)) - { - double[] d = { - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(d); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(d); - } - else - { - RunObjectTypeArrayTest(d); - } - } - else if (testType == typeof(ByteEnum)) - { - ByteEnum[] e = { - ByteEnum.C, - ByteEnum.A, - ByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(SByteEnum)) - { - SByteEnum[] e = { - SByteEnum.C, - SByteEnum.A, - SByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ShortEnum)) - { - ShortEnum[] e = { - ShortEnum.C, - ShortEnum.A, - ShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum[] e = { - UShortEnum.C, - UShortEnum.A, - UShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum[] e = { - IntEnum.C, - IntEnum.A, - IntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum[] e = { - UIntEnum.C, - UIntEnum.A, - UIntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum[] e = { - LongEnum.C, - LongEnum.A, - LongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum[] e = { - ULongEnum.C, - ULongEnum.A, - ULongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new[] - { - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new[] - { - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new[] - { - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new[] - { - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Color)) + using (writer) { - var v = new[] - { - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeArrayTest(v); + Assert.Throws(() => { writer.WriteByteSafe(1); }); } } - else if (testType == typeof(Color32)) - { - var v = new[] - { - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - }; + } - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new[] - { - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray2D)) + [Test] + public void WhenCallingWriteBytesSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - var v = new[] - { - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeArrayTest(v); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); } } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); - } - } - - [Test] - public void TestWritingStruct() - { - RunTypeTest(GetTestStruct()); } [Test] - public void TestWritingStructSafe() + public void WhenCallingWriteValueSafeWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - RunTypeTestSafe(GetTestStruct()); - } + var writer = new FastBufferWriter(100, Allocator.Temp); - [Test] - public void TestWritingStructAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - try - { - RunObjectTypeTest(GetTestStruct()); - } - finally + using (writer) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + writer.TryBeginWrite(100); + int i = 1; + using (var context = writer.EnterBitwiseContext()) + { + Assert.Throws(() => { writer.WriteValueSafe(i); }); + } } } [Test] - public void TestWritingStructArray() + public void WhenCallingWriteValueSafeWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTest(arr); - } - - [Test] - public void TestWritingStructArraySafe() - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTestSafe(arr); - } + var writer = new FastBufferWriter(100, Allocator.Temp); - [Test] - public void TestWritingStructArrayAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - try - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunObjectTypeArrayTest(arr); - } - finally + using (writer) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) + { + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); + } } } [Test] - public unsafe void TestWritingString() + public void WhenCallingWriteValueSafeWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var writer = new FastBufferWriter(100, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int)); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); - } + Assert.Throws(() => { writer.WriteValueSafe(""); }); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); } } [Test] - public unsafe void TestWritingStringSafe() + public void WhenCallingWriteByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var writer = new FastBufferWriter(100, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - writer.WriteValueSafe(valueToTest); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - char* underlyingCharArray = (char*)((byte*)writer.GetUnsafePtr() + sizeof(int)); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); + writer.WriteByte(1); } } [Test] - public unsafe void TestWritingStringAsObject() + public void WhenCallingWriteBytesAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var writer = new FastBufferWriter(100, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); using (writer) { - - writer.WriteObject(valueToTest); - - VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); - WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); - - int* sizeValue = (int*)(writer.GetUnsafePtr() + sizeof(byte)); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize + sizeof(byte)); + writer.WriteBytes(bytes, bytes.Length); } } [Test] - public unsafe void TestWritingOneByteString() + public void WhenCallingWriteValueWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var writer = new FastBufferWriter(100, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest, true); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); + writer.WriteValue(i); } } [Test] - public unsafe void TestWritingOneByteStringSafe() + public void WhenCallingWriteValueWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var writer = new FastBufferWriter(100, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - writer.WriteValueSafe(valueToTest, true); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); + writer.WriteValue(bytes); } } [Test] - public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + public void WhenCallingWriteValueWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count); - - var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; - VerifyPositionAndLength(ref writer, count, failMessage); - WriteCheckBytes(ref writer, count, failMessage); - var underlyingArray = writer.ToArray(); - VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, count, failMessage); - VerifyCheckBytes(underlyingArray, count, failMessage); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - mask = (mask << 8) | 0b11111111; + context.WriteBit(true); } - - ulong* checkValue = (ulong*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); + writer.WriteValue(""); } } [Test] - public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) + public void WhenCallingWriteByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count, 2); - var failMessage = $"TestWritingPartialValuesWithOffsets failed with value {valueToTest}"; - - VerifyPositionAndLength(ref writer, count, failMessage); - WriteCheckBytes(ref writer, count, failMessage); - var underlyingArray = writer.ToArray(); - VerifyBytewiseEquality(valueToTest, underlyingArray, 2, 0, count, failMessage); - VerifyCheckBytes(underlyingArray, count, failMessage); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - mask = (mask << 8) | 0b11111111; - } - - ulong* checkValue = (ulong*)writer.GetUnsafePtr(); - Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); + context.WriteBit(true); + } + writer.WriteByteSafe(1); } } [Test] - public void TestToArray() + public void WhenCallingWriteBytesSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); - var array = writer.ToArray(); - var underlyingArray = writer.ToArray(); - for (var i = 0; i < array.Length; ++i) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - Assert.AreEqual(array[i], underlyingArray[i]); + context.WriteBit(true); } + writer.WriteBytesSafe(bytes, bytes.Length); } } [Test] - public void TestThrowingIfBoundsCheckingSkipped() + public void WhenCallingWriteValueSafeWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - Assert.Throws(() => { writer.WriteByte(1); }); + writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); int i = 1; - Assert.Throws(() => { writer.WriteValue(i); }); - Assert.Throws(() => { writer.WriteValue(bytes); }); - Assert.Throws(() => { writer.WriteValue(""); }); - - writer.VerifyCanWrite(sizeof(int) - 1); - Assert.Throws(() => { writer.WriteValue(i); }); - writer.WriteByte(1); - writer.WriteByte(2); - writer.WriteByte(3); - Assert.Throws(() => { writer.WriteByte(4); }); + using (var context = writer.EnterBitwiseContext()) + { + context.WriteBit(true); + } + writer.WriteValueSafe(i); } } [Test] - public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() + public void WhenCallingWriteValueSafeWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(100); + writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; int i = 1; using (var context = writer.EnterBitwiseContext()) { - Assert.Throws(() => { writer.WriteByte(1); }); - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); - Assert.Throws(() => { writer.WriteValue(i); }); - Assert.Throws(() => { writer.WriteValue(bytes); }); - Assert.Throws(() => { writer.WriteValue(""); }); - - Assert.Throws(() => { writer.WriteByteSafe(1); }); - Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); - Assert.Throws(() => { writer.WriteValueSafe(i); }); - Assert.Throws(() => { writer.WriteValueSafe(bytes); }); - Assert.Throws(() => { writer.WriteValueSafe(""); }); + context.WriteBit(true); } - writer.WriteByte(1); - writer.WriteBytes(bytes, bytes.Length); - writer.WriteValue(i); - writer.WriteValue(bytes); - writer.WriteValue(""); - - writer.WriteByteSafe(1); - writer.WriteBytesSafe(bytes, bytes.Length); - writer.WriteValueSafe(i); writer.WriteValueSafe(bytes); + } + } + + [Test] + public void WhenCallingWriteValueSafeWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) + { + context.WriteBit(true); + } writer.WriteValueSafe(""); } } [Test] - public void TestVerifyCanWriteIsRelativeToPositionAndNotAllowedWritePosition() + public void WhenCallingTryBeginWrite_TheAllowedWritePositionIsMarkedRelativeToCurrentPosition() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(100); + writer.TryBeginWrite(100); writer.WriteByte(1); - writer.VerifyCanWrite(1); + writer.TryBeginWrite(1); writer.WriteByte(1); Assert.Throws(() => { writer.WriteByte(1); }); } } [Test] - public void TestSeeking() + public void WhenWritingAfterSeeking_TheNewWriteGoesToTheCorrectPosition() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) @@ -1853,16 +824,13 @@ public void TestSeeking() writer.Seek(5); writer.WriteByteSafe(0); Assert.AreEqual(writer.Position, 6); - Assert.AreEqual(writer.Length, 6); writer.Seek(0); writer.WriteByteSafe(1); Assert.AreEqual(writer.Position, 1); - Assert.AreEqual(writer.Length, 6); writer.Seek(10); Assert.AreEqual(writer.Position, 10); - Assert.AreEqual(writer.Length, 10); writer.Seek(2); writer.WriteByteSafe(2); @@ -1877,7 +845,6 @@ public void TestSeeking() writer.WriteByteSafe(5); Assert.AreEqual(writer.Position, 4); - Assert.AreEqual(writer.Length, 10); var expected = new byte[] { 1, 3, 2, 5, 4, 0 }; var underlyingArray = writer.ToArray(); @@ -1889,7 +856,35 @@ public void TestSeeking() } [Test] - public void TestTruncate() + public void WhenSeekingForward_LengthUpdatesToNewPosition() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + Assert.AreEqual(writer.Length, 0); + writer.Seek(5); + Assert.AreEqual(writer.Length, 5); + writer.Seek(10); + Assert.AreEqual(writer.Length, 10); + } + } + + [Test] + public void WhenSeekingBackward_LengthDoesNotChange() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + Assert.AreEqual(writer.Length, 0); + writer.Seek(5); + Assert.AreEqual(writer.Length, 5); + writer.Seek(0); + Assert.AreEqual(writer.Length, 5); + } + } + + [Test] + public void WhenTruncatingToSpecificPositionAheadOfWritePosition_LengthIsUpdatedAndPositionIsNot() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) @@ -1905,7 +900,36 @@ public void TestTruncate() writer.Truncate(8); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 8); + } + } + + [Test] + public void WhenTruncatingToSpecificPositionBehindWritePosition_BothLengthAndPositionAreUpdated() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + + writer.Truncate(8); + Assert.AreEqual(writer.Position, 8); + Assert.AreEqual(writer.Length, 8); + } + } + + [Test] + public void WhenTruncatingToCurrentPosition_LengthIsUpdated() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + writer.Seek(5); writer.Truncate(); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 5); @@ -1913,276 +937,263 @@ public void TestTruncate() } [Test] - public void TestCapacity() + public void WhenCreatingNewFastBufferWriter_CapacityIsCorrect() { var writer = new FastBufferWriter(100, Allocator.Temp); Assert.AreEqual(100, writer.Capacity); writer.Dispose(); + + writer = new FastBufferWriter(200, Allocator.Temp); + Assert.AreEqual(200, writer.Capacity); + writer.Dispose(); } [Test] - public void TestGrowth() + public void WhenCreatingNewFastBufferWriter_MaxCapacityIsCorrect() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + Assert.AreEqual(100, writer.MaxCapacity); + writer.Dispose(); + + writer = new FastBufferWriter(100, Allocator.Temp, 200); + Assert.AreEqual(200, writer.MaxCapacity); + writer.Dispose(); + } + + [Test] + public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturnsFalse() { var writer = new FastBufferWriter(150, Allocator.Temp); - var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); - Assert.AreEqual(150, writer.Capacity); using (writer) - using (growingWriter) { var testStruct = GetTestStruct(); - writer.VerifyCanWriteValue(testStruct); + writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - growingWriter.VerifyCanWriteValue(testStruct); - growingWriter.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - var preGrowthLength = writer.Position; - - // First writer isn't allowed to grow because it didn't specify a maxSize - Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); - Assert.Throws(() => writer.WriteValue(testStruct)); - - // Second writer is allowed to grow - Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - - // First writer shouldn't have grown - Assert.AreEqual(150, writer.Capacity); - Assert.AreEqual(preGrowthLength, writer.ToArray().Length); - // First growth doubles the size - Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - growingWriter.WriteValue(testStruct); - - // Write right up to the very end of the buffer, verify it doesn't grow - growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); - Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - growingWriter.WriteValue(testStruct); - - // Go to the end of the buffer and grow again - growingWriter.Seek(300); - Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - growingWriter.WriteValue(testStruct); - - // Second growth caps it at maxSize - Assert.AreEqual(500, growingWriter.Capacity); - Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - - VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - // Verify the growth properly copied the existing data - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300 - FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + + // Writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); } } - private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, - NetworkObject networkObject); - private void RunGameObjectTest(GameObjectTestDelegate testCode) + [Test] + public void WhenTryBeginWriteReturnsFalse_WritingThrowsOverflowException() { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartHost(); - - try - { - testCode(obj, networkBehaviour, networkObject); - } - finally + var writer = new FastBufferWriter(150, Allocator.Temp); + using (writer) { - UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopHost(); + var testStruct = GetTestStruct(); + writer.TryBeginWriteValue(testStruct); + writer.WriteValue(testStruct); + + // Seek to exactly where the write would cross the buffer boundary + writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + + // Writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); + Assert.Throws(() => writer.WriteValue(testStruct)); } } [Test] - public void TestNetworkBehaviour() + public void WhenTryBeginWriteReturnsFalseAndOverflowExceptionIsThrown_DataIsNotAffected() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var writer = new FastBufferWriter(150, Allocator.Temp); + using (writer) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); - - writer.WriteValue(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong), sizeof(ushort)); - } - }); + var testStruct = GetTestStruct(); + writer.TryBeginWriteValue(testStruct); + writer.WriteValue(testStruct); + + // Seek to exactly where the write would cross the buffer boundary + writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + + // Writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); + Assert.Throws(() => writer.WriteValue(testStruct)); + VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkObject() + public void WhenRequestingWritePastBoundsForGrowingWriter_BufferGrowsWithoutLosingData() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); + using (growingWriter) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); - - writer.WriteValue(networkObject); + var testStruct = GetTestStruct(); + growingWriter.TryBeginWriteValue(testStruct); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); - } + // Seek to exactly where the write would cross the buffer boundary + growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - [Test] - public void TestGameObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); - writer.WriteValue(obj); + // Growth doubles the size + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); + // Verify the growth properly copied the existing data + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1, FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkBehaviourSafe() + public void WhenRequestingWriteExactlyAtBoundsForGrowingWriter_BufferDoesntGrow() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var growingWriter = new FastBufferWriter(300, Allocator.Temp, 500); + using (growingWriter) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(networkBehaviour); + var testStruct = GetTestStruct(); + growingWriter.TryBeginWriteValue(testStruct); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong), sizeof(ushort)); - } - }); + growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); + growingWriter.WriteValue(testStruct); + Assert.AreEqual(300, growingWriter.Position); + + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300 - FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkObjectSafe() + public void WhenBufferGrows_MaxCapacityIsNotExceeded() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var growingWriter = new FastBufferWriter(300, Allocator.Temp, 500); + using (growingWriter) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(networkObject); + var testStruct = GetTestStruct(); + growingWriter.TryBeginWriteValue(testStruct); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); - } + growingWriter.Seek(300); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); - [Test] - public void TestGameObjectSafe() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(obj); + Assert.AreEqual(500, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 300); + + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkBehaviourAsObject() + public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); using (writer) { - writer.WriteObject(networkBehaviour); + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); + + var offset = 0; + switch (writeType) + { + case WriteType.WriteDirect: + writer.WriteValue(networkBehaviour); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkBehaviour); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkBehaviour); + // account for isNull byte + offset = 1; + break; + } - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+offset, writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong) + 1, sizeof(ushort)); + sizeof(ulong) + offset, sizeof(ushort)); } }); } [Test] - public void TestNetworkObjectAsObject() + public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); using (writer) { - writer.WriteObject(networkObject); + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); + + var offset = 0; + switch (writeType) + { + case WriteType.WriteDirect: + writer.WriteValue(networkObject); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkObject); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkObject); + // account for isNull byte + offset = 1; + break; + } - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject)+offset, writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); } [Test] - public void TestGameObjectAsObject() + public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); using (writer) { - writer.WriteObject(obj); + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); + + var offset = 0; + switch (writeType) + { + case WriteType.WriteDirect: + writer.WriteValue(obj); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(obj); + break; + case WriteType.WriteAsObject: + writer.WriteObject(obj); + // account for isNull byte + offset = 1; + break; + } - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj)+offset, writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); } [Test] - public void TestVerifyInternalDoesntReduceAllowedWritePoint() + public void WhenCallingTryBeginWriteInternal_AllowedWritePositionDoesNotMoveBackward() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(25); - writer.VerifyCanWriteInternal(5); + writer.TryBeginWrite(25); + writer.TryBeginWriteInternal(5); Assert.AreEqual(writer.AllowedWriteMark, 25); } } diff --git a/testproject/.gitignore b/testproject/.gitignore index acbbe841e6..fbe0ca9b9e 100644 --- a/testproject/.gitignore +++ b/testproject/.gitignore @@ -69,5 +69,7 @@ crashlytics-build.properties # Temporary auto-generated Android Assets /[Aa]ssets/[Ss]treamingAssets/aa.meta /[Aa]ssets/[Ss]treamingAssets/aa/* +/[Aa]ssets/[Ss]treamingAssets/BuildInfo.json +/[Aa]ssets/[Ss]treamingAssets/BuildInfo.json.meta InitTestScene* diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs index 508d5a341b..8bfae869b2 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs @@ -11,7 +11,7 @@ namespace TestProject.RuntimeTests { - public class NetworkSceneManagerTests : BaseMultiInstanceTest + public class NetworkSceneManagerTests: BaseMultiInstanceTest { protected override int NbClients => 9; @@ -224,15 +224,15 @@ private void SetClientWaitDone(List clients) private bool ContainsAllClients(List clients) { // First, make sure we have the expected client count - if (clients.Count != m_ShouldWaitList.Count) + if(clients.Count != m_ShouldWaitList.Count) { return false; } // Next, make sure we have all client identifiers - foreach (var sceneTestInfo in m_ShouldWaitList) + foreach(var sceneTestInfo in m_ShouldWaitList) { - if (!clients.Contains(sceneTestInfo.ClientId)) + if(!clients.Contains(sceneTestInfo.ClientId)) { return false; } @@ -248,12 +248,12 @@ private bool ContainsAllClients(List clients) /// private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) { - switch (sceneEvent.SceneEventType) + switch(sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Load: case SceneEventData.SceneEventTypes.S2C_Unload: { - Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); + Assert.AreEqual(sceneEvent.SceneName,m_CurrentSceneName); Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); Assert.IsNotNull(sceneEvent.AsyncOperation); break; From 9816b50b728b93ec6564f9e2b80f0d5a559b6c21 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 11:14:50 -0500 Subject: [PATCH 18/58] standards.py --fix --- .../Serialization/FastBufferWriterTests.cs | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 37e93eb59d..af4adeb5f1 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -121,7 +121,7 @@ protected override unsafe void RunObjectTypeTest(T valueToTest) } } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged { int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); @@ -260,25 +260,25 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, // account for isNull byte offset = sizeof(byte); break; - + } - VerifyPositionAndLength(ref writer, serializedValueSize+offset); - WriteCheckBytes(ref writer, serializedValueSize+offset); + VerifyPositionAndLength(ref writer, serializedValueSize + offset); + WriteCheckBytes(ref writer, serializedValueSize + offset); int* sizeValue = (int*)(writer.GetUnsafePtr() + offset); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - if(oneByteChars) + if (oneByteChars) { byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int) + offset; for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); } - + } else { @@ -291,7 +291,7 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, } var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize+offset); + VerifyCheckBytes(underlyingArray, serializedValueSize + offset); } } @@ -303,7 +303,7 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, [TestCase(6, 0)] [TestCase(7, 0)] [TestCase(8, 0)] - + [TestCase(1, 1)] [TestCase(2, 1)] [TestCase(3, 1)] @@ -311,32 +311,32 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, [TestCase(5, 1)] [TestCase(6, 1)] [TestCase(7, 1)] - + [TestCase(1, 2)] [TestCase(2, 2)] [TestCase(3, 2)] [TestCase(4, 2)] [TestCase(5, 2)] [TestCase(6, 2)] - + [TestCase(1, 3)] [TestCase(2, 3)] [TestCase(3, 3)] [TestCase(4, 3)] [TestCase(5, 3)] - + [TestCase(1, 4)] [TestCase(2, 4)] [TestCase(3, 4)] [TestCase(4, 4)] - + [TestCase(1, 5)] [TestCase(2, 5)] [TestCase(3, 5)] - + [TestCase(1, 6)] [TestCase(2, 6)] - + [TestCase(1, 7)] public unsafe void WhenWritingPartialValueWithCountAndOffset_ValueIsWrittenCorrectly(int count, int offset) { @@ -942,7 +942,7 @@ public void WhenCreatingNewFastBufferWriter_CapacityIsCorrect() var writer = new FastBufferWriter(100, Allocator.Temp); Assert.AreEqual(100, writer.Capacity); writer.Dispose(); - + writer = new FastBufferWriter(200, Allocator.Temp); Assert.AreEqual(200, writer.Capacity); writer.Dispose(); @@ -954,7 +954,7 @@ public void WhenCreatingNewFastBufferWriter_MaxCapacityIsCorrect() var writer = new FastBufferWriter(100, Allocator.Temp); Assert.AreEqual(100, writer.MaxCapacity); writer.Dispose(); - + writer = new FastBufferWriter(100, Allocator.Temp, 200); Assert.AreEqual(200, writer.MaxCapacity); writer.Dispose(); @@ -969,10 +969,10 @@ public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturn var testStruct = GetTestStruct(); writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - + // Writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); } @@ -987,10 +987,10 @@ public void WhenTryBeginWriteReturnsFalse_WritingThrowsOverflowException() var testStruct = GetTestStruct(); writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - + // Writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); Assert.Throws(() => writer.WriteValue(testStruct)); @@ -1006,10 +1006,10 @@ public void WhenTryBeginWriteReturnsFalseAndOverflowExceptionIsThrown_DataIsNotA var testStruct = GetTestStruct(); writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - + // Writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); Assert.Throws(() => writer.WriteValue(testStruct)); @@ -1080,7 +1080,7 @@ public void WhenBufferGrows_MaxCapacityIsNotExceeded() Assert.AreEqual(500, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, 300); - + growingWriter.WriteValue(testStruct); VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); @@ -1093,7 +1093,7 @@ public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); using (writer) { Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); @@ -1114,7 +1114,7 @@ public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values break; } - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+offset, writer.Position); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + offset, writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong) + offset, sizeof(ushort)); @@ -1127,7 +1127,7 @@ public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject) + 1, Allocator.Temp); using (writer) { Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); @@ -1148,7 +1148,7 @@ public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType break; } - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject)+offset, writer.Position); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject) + offset, writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); @@ -1159,7 +1159,7 @@ public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType wr { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj) + 1, Allocator.Temp); using (writer) { Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); @@ -1180,7 +1180,7 @@ public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType wr break; } - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj)+offset, writer.Position); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj) + offset, writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); From a576552feb61cef07abeda6448e1d75017cfc125 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 13:20:58 -0500 Subject: [PATCH 19/58] standards.py --fix --- .../Runtime/Serialization/FastBufferReader.cs | 2 - .../FastBufferReaderExtensions.cs | 6 +-- .../Runtime/Serialization/FastBufferWriter.cs | 2 - .../FastBufferWriterExtensions.cs | 6 +-- .../BaseFastBufferReaderWriterTest.cs | 14 +++--- .../Editor/Serialization/BitCounterTests.cs | 8 ++-- .../Serialization/FastBufferReaderTests.cs | 44 +++++++++---------- 7 files changed, 38 insertions(+), 44 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 31493bc2e6..77adda8aab 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -2,8 +2,6 @@ using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode; -using UnityEngine; namespace Unity.Multiplayer.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 21d7daf745..43752c915e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -1,4 +1,4 @@ - + using System; using Unity.Netcode; using UnityEngine; @@ -7,7 +7,7 @@ namespace Unity.Multiplayer.Netcode { public static class FastBufferReaderExtensions { - + /// /// Reads a boxed object in a standard format /// Named differently from other ReadValue methods to avoid accidental boxing @@ -284,4 +284,4 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBe value = null; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 22fea00ced..2504dedd55 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -2,8 +2,6 @@ using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode; -using UnityEngine; namespace Unity.Multiplayer.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs index 3fea8ee5c0..c29f7f302a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -1,4 +1,4 @@ - + using System; using Unity.Netcode; using UnityEngine; @@ -7,7 +7,7 @@ namespace Unity.Multiplayer.Netcode { public static class FastBufferWriterExtensions { - + /// /// Writes a boxed object in a standard format /// Named differently from other WriteValue methods to avoid accidental boxing @@ -295,4 +295,4 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehav } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index e2d22d75b3..315c24a7ae 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using Unity.Multiplayer.Netcode; @@ -11,7 +11,7 @@ namespace Unity.Netcode { public abstract class BaseFastBufferReaderWriterTest { - + #region Test Types protected enum ByteEnum : byte { @@ -97,7 +97,7 @@ public enum WriteType protected abstract void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged; protected abstract void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged; - + #region Helpers protected TestStruct GetTestStruct() { @@ -120,7 +120,7 @@ protected TestStruct GetTestStruct() return testStruct; } - + protected delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, NetworkObject networkObject); protected void RunGameObjectTest(GameObjectTestDelegate testCode) @@ -155,11 +155,11 @@ protected void RunGameObjectTest(GameObjectTestDelegate testCode) } } #endregion - + public void BaseTypeTest(Type testType, WriteType writeType) { var random = new Random(); - + void RunTypeTestLocal(T val, WriteType wt) where T : unmanaged { switch (wt) @@ -666,4 +666,4 @@ void RunTypeTestLocal(T[] val, WriteType wt) where T : unmanaged } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 96d68f8c67..0aca9b397d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -36,7 +36,7 @@ public void WhenCountingUsedBitsIn32BitValue_ResultMatchesHighBitSetPlusOne([Ran Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value)); } } - + [Test] public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 63)] int highBit) { @@ -49,10 +49,10 @@ public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOn else { ulong value = 1UL << highBit; - Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(highBit / 8 + 1, BitCounter.GetUsedByteCount(value)); } } - + [Test] public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 31)] int highBit) { @@ -65,7 +65,7 @@ public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOn else { uint value = 1U << highBit; - Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(highBit / 8 + 1, BitCounter.GetUsedByteCount(value)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index cf0817f507..f794176d62 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; using Unity.Collections; using Unity.Multiplayer.Netcode; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode.EditorTests @@ -292,7 +290,7 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(string), oneByteChars); - result = (string) resultObj; + result = (string)resultObj; break; } Assert.AreEqual(valueToTest, result); @@ -311,7 +309,7 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW [TestCase(6, 0)] [TestCase(7, 0)] [TestCase(8, 0)] - + [TestCase(1, 1)] [TestCase(2, 1)] [TestCase(3, 1)] @@ -319,32 +317,32 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW [TestCase(5, 1)] [TestCase(6, 1)] [TestCase(7, 1)] - + [TestCase(1, 2)] [TestCase(2, 2)] [TestCase(3, 2)] [TestCase(4, 2)] [TestCase(5, 2)] [TestCase(6, 2)] - + [TestCase(1, 3)] [TestCase(2, 3)] [TestCase(3, 3)] [TestCase(4, 3)] [TestCase(5, 3)] - + [TestCase(1, 4)] [TestCase(2, 4)] [TestCase(3, 4)] [TestCase(4, 4)] - + [TestCase(1, 5)] [TestCase(2, 5)] [TestCase(3, 5)] - + [TestCase(1, 6)] [TestCase(2, 6)] - + [TestCase(1, 7)] public void GivenFastBufferWriterContainingValue_WhenReadingPartialValue_ValueMatchesWhatWasWritten(int count, int offset) { @@ -425,7 +423,7 @@ public void WhenCallingReadBytesWithoutCallingTryBeingReadFirst_OverflowExceptio var nativeArray = new NativeArray(100, Allocator.Temp); var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - byte[] b = {0, 1, 2}; + byte[] b = { 0, 1, 2 }; using (emptyReader) { Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); @@ -522,7 +520,7 @@ public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIs { using (var context = emptyReader.EnterBitwiseContext()) { - byte[] b = {0, 1, 2}; + byte[] b = { 0, 1, 2 }; Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } } @@ -598,7 +596,7 @@ public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExcepti { using (var context = emptyReader.EnterBitwiseContext()) { - byte[] b = {0, 1, 2}; + byte[] b = { 0, 1, 2 }; Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); } } @@ -648,7 +646,7 @@ public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperat } } } - + [Test] public void WhenCallingReadByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { @@ -680,7 +678,7 @@ public void WhenCallingReadBytesAfterExitingBitwiseContext_InvalidOperationExcep context.ReadBit(out bool theBit); } - byte[] theBytes = {0, 1, 2}; + byte[] theBytes = { 0, 1, 2 }; emptyReader.ReadBytes(ref theBytes, 3); } } @@ -735,7 +733,7 @@ public void WhenCallingReadValueWithStringAfterExitingBitwiseContext_InvalidOper emptyReader.ReadValue(out string s); } } - + [Test] public void WhenCallingReadByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { @@ -767,7 +765,7 @@ public void WhenCallingReadBytesSafeAfterExitingBitwiseContext_InvalidOperationE context.ReadBit(out bool theBit); } - byte[] theBytes = {0, 1, 2}; + byte[] theBytes = { 0, 1, 2 }; emptyReader.ReadBytesSafe(ref theBytes, 3); } } @@ -898,7 +896,7 @@ public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObje { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject) + 1, Allocator.Temp); using (writer) { switch (writeType) @@ -931,7 +929,7 @@ public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObje break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(NetworkObject)); - result = (NetworkObject) resultObj; + result = (NetworkObject)resultObj; break; } Assert.AreSame(result, networkObject); @@ -945,7 +943,7 @@ public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_The { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj) + 1, Allocator.Temp); using (writer) { switch (writeType) @@ -978,7 +976,7 @@ public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_The break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(GameObject)); - result = (GameObject) resultObj; + result = (GameObject)resultObj; break; } Assert.AreSame(result, obj); @@ -992,7 +990,7 @@ public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkB { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); using (writer) { switch (writeType) @@ -1025,7 +1023,7 @@ public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkB break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(NetworkBehaviour)); - result = (NetworkBehaviour) resultObj; + result = (NetworkBehaviour)resultObj; break; } Assert.AreSame(result, networkBehaviour); From 5193e39b95fb28eca7797b9effc73c44416b132f Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 17:02:25 -0500 Subject: [PATCH 20/58] Fixed incorrect namespaces. --- .../Runtime/Serialization/BitCounter.cs | 2 +- .../Runtime/Serialization/BitReader.cs | 3 +-- .../Runtime/Serialization/BitWriter.cs | 2 +- .../Runtime/Serialization/BufferSerializer.cs | 3 +-- .../Runtime/Serialization/BufferSerializerReader.cs | 3 +-- .../Runtime/Serialization/BufferSerializerWriter.cs | 3 +-- .../Runtime/Serialization/BytePacker.cs | 3 +-- .../Runtime/Serialization/ByteUnpacker.cs | 3 +-- .../Runtime/Serialization/BytewiseUtility.cs | 2 +- .../Runtime/Serialization/FastBufferReader.cs | 2 +- .../Runtime/Serialization/FastBufferReaderExtensions.cs | 3 +-- .../Runtime/Serialization/FastBufferWriter.cs | 2 +- .../Runtime/Serialization/FastBufferWriterExtensions.cs | 3 +-- .../Runtime/Serialization/IBufferSerializerImplementation.cs | 3 +-- .../Runtime/Serialization/SerializationTypeTable.cs | 2 +- com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs | 2 +- com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs | 2 +- .../Editor/Serialization/BaseFastBufferReaderWriterTest.cs | 1 - .../Tests/Editor/Serialization/BitCounterTests.cs | 1 - .../Tests/Editor/Serialization/BitReaderTests.cs | 1 - .../Tests/Editor/Serialization/BitWriterTests.cs | 1 - .../Tests/Editor/Serialization/BufferSerializerTests.cs | 2 -- .../Tests/Editor/Serialization/BytePackerTests.cs | 1 - .../Tests/Editor/Serialization/FastBufferReaderTests.cs | 2 -- .../Tests/Editor/Serialization/FastBufferWriterTests.cs | 1 - 25 files changed, 17 insertions(+), 36 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs index fad7dcf093..4feb4477da 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class BitCounter { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 3540ea73fd..411152046d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,8 +1,7 @@ using System; using System.Runtime.CompilerServices; -using Unity.Netcode; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Helper class for doing bitwise reads for a FastBufferReader. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 3183f6ce33..76a7ecab2e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Helper class for doing bitwise writes for a FastBufferWriter. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index bba0c112c9..a9132cb5bd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { /// /// Two-way serializer wrapping FastBufferReader or FastBufferWriter. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index 495212dfa9..ae777d3735 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { internal struct BufferSerializerReader : IBufferSerializerImplementation { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index f6f63b95c2..f3a345ea99 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { internal struct BufferSerializerWriter : IBufferSerializerImplementation { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 39f031d220..9eb5854e67 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -1,9 +1,8 @@ using System; using System.Runtime.CompilerServices; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Utility class for packing values in serialization. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index b83bca1062..857829e126 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -1,9 +1,8 @@ using System; using System.Runtime.CompilerServices; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class ByteUnpacker { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs index 36394addad..0732fca31e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -1,6 +1,6 @@ using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class BytewiseUtility { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 77adda8aab..070e209a38 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public struct FastBufferReader : IDisposable { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 43752c915e..02f567a9df 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -1,9 +1,8 @@ using System; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class FastBufferReaderExtensions { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 2504dedd55..4fb9e9a5a5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public struct FastBufferWriter : IDisposable { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs index c29f7f302a..af269f811e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -1,9 +1,8 @@ using System; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class FastBufferWriterExtensions { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index 7d181d3f11..d97954c243 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { public interface IBufferSerializerImplementation { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index c3e497281e..fb861c91d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Registry for telling FastBufferWriter and FastBufferReader how to read types when passed to diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index 62ae287a3a..02572cbc46 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public struct Ref where T : unmanaged { diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs index bac5669ad9..52d456e4a3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public ref struct RefArray where T : unmanaged { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index 315c24a7ae..ae5e1aefa0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using Unity.Multiplayer.Netcode; using Unity.Netcode.EditorTests; using UnityEngine; using UnityEngine.SceneManagement; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 0aca9b397d..5e0f4c8a12 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,5 +1,4 @@ using NUnit.Framework; -using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index 533c6bf963..87b0154ee5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index 612fcb1f2f..cd51c356de 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index aa24d4e030..0f050f6301 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; -using Unity.Netcode.Serialization; using UnityEngine; using UnityEngine.SceneManagement; using Random = System.Random; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 0ece584a51..1c2ff159e9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -3,7 +3,6 @@ using System.Reflection; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; using UnityEngine; using Random = System.Random; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index f794176d62..52a9180c1e 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -1,8 +1,6 @@ using System; using NUnit.Framework; -using NUnit.Framework.Internal; using Unity.Collections; -using Unity.Multiplayer.Netcode; using UnityEngine; using Random = System.Random; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index af4adeb5f1..acdb2af153 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; using UnityEngine; using Random = System.Random; From 119ee2f0e0715ddeeb646d67120ee7ee237a4081 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 18:35:14 -0500 Subject: [PATCH 21/58] -Fixed a couple of issues where growing a FastBufferWriter wouldn't work correctly (requesting beyond MaxCapacity and requesting more than double current capacity) -Added support for FastBufferReader to be used in a mode that doesn't copy the input buffer --- .../Runtime/Serialization/BitWriter.cs | 6 +- .../Runtime/Serialization/FastBufferReader.cs | 61 ++++++++++++++----- .../Runtime/Serialization/FastBufferWriter.cs | 22 +++++-- .../Serialization/FastBufferWriterTests.cs | 31 ++++++++++ 4 files changed, 100 insertions(+), 20 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 76a7ecab2e..688210a9f5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -80,9 +80,13 @@ public unsafe bool TryBeginWriteBits(int bitCount) if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.CapacityInternal) { + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.MaxCapacityInternal) + { + return false; + } if (m_Writer.Value.CapacityInternal < m_Writer.Value.MaxCapacityInternal) { - m_Writer.Value.Grow(); + m_Writer.Value.Grow(totalBytesWrittenInBitwiseContext); m_BufferPointer = m_Writer.Value.BufferPointer + m_Writer.Value.PositionInternal; } else diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 070e209a38..9b9f7b7f2a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -46,9 +46,16 @@ internal void CommitBitwiseReads(int amount) public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, int length = -1, int offset = 0) { LengthInternal = Math.Max(1, length == -1 ? buffer.Length : length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr() + offset, LengthInternal); - BufferPointer = (byte*)bufferPtr; + if (allocator == Allocator.None) + { + BufferPointer = (byte*) buffer.GetUnsafePtr() + offset; + } + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr() + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + } PositionInternal = offset; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -69,12 +76,21 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) { LengthInternal = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - fixed (byte* data = buffer.Array) + if (allocator == Allocator.None) { - UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + throw new NotSupportedException("Allocator.None cannot be used with managed source buffers."); } - BufferPointer = (byte*)bufferPtr; + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer.Array) + { + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + } + + BufferPointer = (byte*) bufferPtr; + } + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -95,12 +111,21 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { LengthInternal = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - fixed (byte* data = buffer) + if (allocator == Allocator.None) { - UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + throw new NotSupportedException("Allocator.None cannot be used with managed source buffers."); } - BufferPointer = (byte*)bufferPtr; + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer) + { + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + } + + BufferPointer = (byte*) bufferPtr; + } + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -121,9 +146,17 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) { LengthInternal = Math.Max(1, length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); - BufferPointer = (byte*)bufferPtr; + if (allocator == Allocator.None) + { + BufferPointer = buffer + offset; + } + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); + BufferPointer = (byte*) bufferPtr; + } + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 4fb9e9a5a5..b2e44dd0dd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -159,9 +159,9 @@ public BitWriter EnterBitwiseContext() return new BitWriter(ref this); } - internal unsafe void Grow() + internal unsafe void Grow(int additionalSizeRequired) { - var newSize = Math.Min(CapacityInternal * 2, MaxCapacityInternal); + var newSize = Math.Min(Math.Max(CapacityInternal * 2, (Position + additionalSizeRequired) * 2), MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, newSize); @@ -198,9 +198,13 @@ public bool TryBeginWrite(int bytes) #endif if (PositionInternal + bytes > CapacityInternal) { + if (PositionInternal + bytes > MaxCapacityInternal) + { + return false; + } if (CapacityInternal < MaxCapacityInternal) { - Grow(); + Grow(bytes); } else { @@ -241,9 +245,13 @@ public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged int len = sizeof(T); if (PositionInternal + len > CapacityInternal) { + if (PositionInternal + len > MaxCapacityInternal) + { + return false; + } if (CapacityInternal < MaxCapacityInternal) { - Grow(); + Grow(len); } else { @@ -275,9 +283,13 @@ public bool TryBeginWriteInternal(int bytes) #endif if (PositionInternal + bytes > CapacityInternal) { + if (PositionInternal + bytes > MaxCapacityInternal) + { + return false; + } if (CapacityInternal < MaxCapacityInternal) { - Grow(); + Grow(bytes); } else { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index acdb2af153..b68de099f5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1087,6 +1087,37 @@ public void WhenBufferGrows_MaxCapacityIsNotExceeded() } } + [Test] + public void WhenBufferGrowthRequiredIsMoreThanDouble_BufferGrowsEnoughToContainRequestedValue() + { + var growingWriter = new FastBufferWriter(1, Allocator.Temp, 500); + using (growingWriter) + { + var testStruct = GetTestStruct(); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(testStruct)*2, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 0); + + growingWriter.WriteValue(testStruct); + + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + } + } + + [Test] + public void WhenTryingToWritePastMaxCapacity_GrowthDoesNotOccurAndTryBeginWriteReturnsFalse() + { + var growingWriter = new FastBufferWriter(300, Allocator.Temp, 500); + using (growingWriter) + { + Assert.IsFalse(growingWriter.TryBeginWrite(501)); + + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 0); + } + } + [Test] public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values] WriteType writeType) { From 7fb2d536b38d16043d4dfbc9980824cdae585ebb Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 27 Aug 2021 10:46:22 -0500 Subject: [PATCH 22/58] Fix a test failure and better implementation of large growths --- .../Runtime/Serialization/FastBufferWriter.cs | 7 ++++++- .../Tests/Editor/Serialization/FastBufferWriterTests.cs | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index b2e44dd0dd..4a6a44092c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -161,7 +161,12 @@ public BitWriter EnterBitwiseContext() internal unsafe void Grow(int additionalSizeRequired) { - var newSize = Math.Min(Math.Max(CapacityInternal * 2, (Position + additionalSizeRequired) * 2), MaxCapacityInternal); + var desiredSize = CapacityInternal * 2; + while (desiredSize < Position + additionalSizeRequired) + { + desiredSize *= 2; + } + var newSize = Math.Min(desiredSize, MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, newSize); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index b68de099f5..54dca7131a 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1096,7 +1096,10 @@ public void WhenBufferGrowthRequiredIsMoreThanDouble_BufferGrowsEnoughToContainR var testStruct = GetTestStruct(); Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); - Assert.AreEqual(FastBufferWriter.GetWriteSize(testStruct)*2, growingWriter.Capacity); + // Buffer size doubles with each growth, so since we're starting with a size of 1, that means + // the resulting size should be the next power of 2 above the size of testStruct. + Assert.AreEqual(Math.Pow(2, Math.Ceiling(Mathf.Log(FastBufferWriter.GetWriteSize(testStruct), 2))), + growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, 0); growingWriter.WriteValue(testStruct); From 8ee461051da34db6e5e7bd48ffee90f2a335e86b Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 31 Aug 2021 13:50:34 -0500 Subject: [PATCH 23/58] - Removed RefArray - Fixed incorrect text in a warning in FastBufferReader --- .../Runtime/Serialization/FastBufferReader.cs | 2 +- .../Runtime/Utility/RefArray.cs | 92 ------------------- 2 files changed, 1 insertion(+), 93 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 9b9f7b7f2a..88c36a1b61 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -693,7 +693,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged } if (PositionInternal + len > AllowedReadMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs deleted file mode 100644 index 52d456e4a3..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Unity.Netcode -{ - public ref struct RefArray where T : unmanaged - { - public struct RefArrayImplementation : IReadOnlyList - where T : unmanaged - { - private unsafe T* m_Value; - private int m_Length; - - internal unsafe RefArrayImplementation(T* ptr, int length) - { - m_Value = ptr; - m_Length = length; - } - - public unsafe ref T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref *m_Value; - } - - public struct Enumerator : IEnumerator, IEnumerator, IDisposable - { - private RefArrayImplementation m_Array; - private int m_Index; - - public Enumerator(ref RefArrayImplementation array) - { - m_Array = array; - m_Index = -1; - } - - public void Dispose() - { - } - - public bool MoveNext() - { - ++m_Index; - return m_Index < m_Array.Length; - } - - public void Reset() => m_Index = -1; - - public T Current => m_Array[m_Index]; - - object IEnumerator.Current => (object)Current; - } - - public Enumerator GetEnumerator() => - new Enumerator(ref this); - - IEnumerator IEnumerable.GetEnumerator() => - (IEnumerator)new Enumerator(ref this); - - IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); - - public int Count => m_Length; - public int Length => m_Length; - - public unsafe T this[int index] - { - get => m_Value[index]; - set => m_Value[index] = value; - } - } - - private RefArrayImplementation m_Value; - - public unsafe RefArray(T* ptr, int length) - { - m_Value = new RefArrayImplementation(ptr, length); - } - - public unsafe ref RefArrayImplementation Value - { - get - { - fixed (RefArrayImplementation* ptr = &m_Value) - { - return ref *ptr; - } - } - } - } -} From 808f28a549e010e29d36b98926523ca400bfd528 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 31 Aug 2021 15:51:52 -0500 Subject: [PATCH 24/58] First INetworkMessage WIP - not hooked up to anything yet. --- .../Runtime/Messaging/BatchHeader.cs | 7 + .../BatchHeader.cs.meta} | 2 +- .../Runtime/Messaging/Bind.cs | 14 + .../Runtime/Messaging/Bind.cs.meta | 11 + .../Runtime/Messaging/DelayUtility.cs | 120 +++++ .../Runtime/Messaging/DelayUtility.cs.meta | 11 + .../Runtime/Messaging/IMessageHandler.cs | 8 + .../Runtime/Messaging/IMessageHandler.cs.meta | 11 + .../Runtime/Messaging/IMessageSender.cs | 7 + .../Runtime/Messaging/IMessageSender.cs.meta | 11 + .../Runtime/Messaging/INetworkHooks.cs | 12 + .../Runtime/Messaging/INetworkHooks.cs.meta | 11 + .../Runtime/Messaging/INetworkMessage.cs | 7 + .../Runtime/Messaging/INetworkMessage.cs.meta | 11 + .../Runtime/Messaging/MessageHeader.cs | 9 + .../Runtime/Messaging/MessageHeader.cs.meta | 11 + .../Runtime/Messaging/MessagingSystem.cs | 440 ++++++++++++++++++ .../Runtime/Messaging/MessagingSystem.cs.meta | 11 + .../Runtime/Messaging/NetworkContext.cs | 11 + .../Runtime/Messaging/NetworkContext.cs.meta | 11 + .../Messaging/NetworkManagerMessageSender.cs | 49 ++ .../NetworkManagerMessageSender.cs.meta | 11 + .../Runtime/Utility/DynamicUnmanagedArray.cs | 104 +++++ .../Utility/DynamicUnmanagedArray.cs.meta | 11 + .../Runtime/Utility/Ref.cs | 11 + .../Runtime/com.unity.netcode.runtime.asmdef | 3 +- .../Tests/Editor/Messaging.meta | 3 + .../Editor/Messaging/DelayUtilityTests.cs | 205 ++++++++ .../Messaging/DelayUtilityTests.cs.meta | 11 + .../Editor/Messaging/MessageReceivingTests.cs | 206 ++++++++ .../Messaging/MessageReceivingTests.cs.meta | 11 + .../Messaging/MessageRegistrationTests.cs | 244 ++++++++++ .../MessageRegistrationTests.cs.meta | 11 + .../Editor/Messaging/MessageSendingTests.cs | 211 +++++++++ .../Messaging/MessageSendingTests.cs.meta | 11 + .../Editor/Messaging/NopMessageSender.cs | 9 + .../Editor/Messaging/NopMessageSender.cs.meta | 11 + 37 files changed, 1856 insertions(+), 2 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs rename com.unity.netcode.gameobjects/Runtime/{Utility/RefArray.cs.meta => Messaging/BatchHeader.cs.meta} (83%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs new file mode 100644 index 0000000000..88764352d1 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs @@ -0,0 +1,7 @@ +namespace Unity.Netcode +{ + public struct BatchHeader + { + public ushort BatchSize; + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs.meta index 9e30a4c74c..b96a0b8213 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1422b1128ad62024d9e8dac38e009aad +guid: 941fcfe2222f8734ab5bfb9bc4787717 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs new file mode 100644 index 0000000000..f2c550d114 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs @@ -0,0 +1,14 @@ +using System; + +namespace Unity.Netcode +{ + public class Bind : Attribute + { + public Type BoundType; + + public Bind(Type boundType) + { + BoundType = boundType; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta new file mode 100644 index 0000000000..76e8e8e1a8 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9697b6526afcb8a429e65b78d4453e90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs new file mode 100644 index 0000000000..3930747356 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using Unity.Collections; + +namespace Unity.Netcode +{ + public class DelayUtility: IDisposable + { + public enum DelayStage: byte + { + PreFixedUpdate = NetworkUpdateStage.FixedUpdate, + PreUpdate = NetworkUpdateStage.Update, + PreLateUpdate = NetworkUpdateStage.PreLateUpdate, + PostLateUpdate = NetworkUpdateStage.PostLateUpdate + } + + public delegate void DelayedNetworkAction(); + + private Dictionary> m_DelayedCallbacks = new Dictionary> + { + [DelayStage.PreFixedUpdate] = new List(), + [DelayStage.PreUpdate] = new List(), + [DelayStage.PreLateUpdate] = new List(), + [DelayStage.PostLateUpdate] = new List(), + }; + + private struct DelayedMessageHandlerData + { + public FastBufferReader Reader; + public MessageHeader Header; + public ulong SenderId; + public float Timestamp; + } + + private NativeHashMap>> m_DelayedMessageHandlers = + new NativeHashMap>>((int)DelayStage.PostLateUpdate, Allocator.Persistent); + + private IMessageHandler m_MessageHandler; + private bool m_disposed; + + public DelayUtility(IMessageHandler messageHandler) + { + m_MessageHandler = messageHandler; + m_DelayedMessageHandlers[(byte) DelayStage.PreFixedUpdate] = + DynamicUnmanagedArray.CreateRef(); + m_DelayedMessageHandlers[(byte) DelayStage.PreUpdate] = + DynamicUnmanagedArray.CreateRef(); + m_DelayedMessageHandlers[(byte) DelayStage.PreLateUpdate] = + DynamicUnmanagedArray.CreateRef(); + m_DelayedMessageHandlers[(byte) DelayStage.PostLateUpdate] = + DynamicUnmanagedArray.CreateRef(); + } + + public void Dispose() + { + if (!m_disposed) + { + foreach (var handler in m_DelayedMessageHandlers) + { + handler.Value.Value.Dispose(); + } + m_DelayedMessageHandlers.Dispose(); + m_disposed = true; + } + } + + ~DelayUtility() + { + Dispose(); + } + + public void DelayUntil(DelayStage stage, DelayedNetworkAction callback) + { + m_DelayedCallbacks[stage].Add(callback); + } + + public enum DelayResult + { + Delay, + Continue + } + + + public unsafe DelayResult DelayUntil(DelayStage stage, ref FastBufferReader reader, NetworkContext context) + { + if ((NetworkUpdateStage)stage == NetworkUpdateLoop.UpdateStage) + { + return DelayResult.Continue; + } + m_DelayedMessageHandlers[(byte)stage].Value.Add(new DelayedMessageHandlerData + { + // Have to make a copy so we can manage the lifetime. + Reader = new FastBufferReader(reader.GetUnsafePtr(), Allocator.TempJob, reader.Length), + Header = context.Header, + SenderId = context.SenderId, + Timestamp = context.Timestamp + }); + return DelayResult.Delay; + } + + public void NetworkUpdate(NetworkUpdateStage stage) + { + ref var delayedMessageHandlers = ref m_DelayedMessageHandlers[(byte) stage].Value; + for (var i = 0; i < delayedMessageHandlers.Count; ++i) + { + ref var data = ref delayedMessageHandlers.GetValueRef(i); + m_MessageHandler.HandleMessage(data.Header, ref data.Reader, data.SenderId, data.Timestamp); + } + delayedMessageHandlers.Clear(); + + var delayedCallbacks = m_DelayedCallbacks[(DelayStage) stage]; + for (var i = 0; i < delayedCallbacks.Count; ++i) + { + delayedCallbacks[i].Invoke(); + } + delayedCallbacks.Clear(); + } + + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta new file mode 100644 index 0000000000..a3b3706ec4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69b372fb7b437f548afc8a419933dab6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs new file mode 100644 index 0000000000..65beef8978 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs @@ -0,0 +1,8 @@ +namespace Unity.Netcode +{ + public interface IMessageHandler + { + public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, + float timestamp); + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta new file mode 100644 index 0000000000..c4db58e397 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24cbf54292ac5c143b08c95aa07e18c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs new file mode 100644 index 0000000000..4a4edee35c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs @@ -0,0 +1,7 @@ +namespace Unity.Netcode +{ + public interface IMessageSender + { + void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData); + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs.meta new file mode 100644 index 0000000000..56d9cc9435 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15b54cd88eba22648ade4240523b8c65 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs new file mode 100644 index 0000000000..e29c199495 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs @@ -0,0 +1,12 @@ +using System; + +namespace Unity.Netcode +{ + public interface INetworkHooks + { + void OnSendMessage(ulong clientId, Type messageType, NetworkChannel channel, NetworkDelivery delivery); + void OnReceiveMessage(ulong senderId, Type messageType, NetworkChannel channel); + void OnSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); + void OnReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs.meta new file mode 100644 index 0000000000..cc2de4ab71 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b199c5a160beabb47bd6b0e4f06cfcd2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs new file mode 100644 index 0000000000..d6f2f11fcd --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs @@ -0,0 +1,7 @@ +namespace Unity.Netcode +{ + public interface INetworkMessage + { + void Serialize(ref FastBufferWriter writer); + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs.meta new file mode 100644 index 0000000000..d6c9a32145 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16079de8d8821a24c91db930bc892b5d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs new file mode 100644 index 0000000000..39b9a05985 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs @@ -0,0 +1,9 @@ +namespace Unity.Netcode +{ + public struct MessageHeader + { + public byte MessageType; + public NetworkChannel NetworkChannel; + public short MessageSize; + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs.meta new file mode 100644 index 0000000000..1931b10738 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74fa727ddec342c48ab49156a32b977b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs new file mode 100644 index 0000000000..63f8aa77af --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -0,0 +1,440 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Netcode.Transports.UNET; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Unity.Netcode +{ + + public class InvalidMessageStructureException : SystemException + { + public InvalidMessageStructureException() { } + public InvalidMessageStructureException(string issue) : base(issue) { } + } + + public class MessagingSystem: IMessageHandler, IDisposable + { +#region Internal Types + private struct ReceiveQueueItem + { + public FastBufferReader Reader; + public MessageHeader Header; + public ulong SenderId; + public float Timestamp; + } + + private struct SendQueueItem + { + public BatchHeader BatchHeader; + public FastBufferWriter Writer; + public readonly NetworkDelivery NetworkDelivery; + + public SendQueueItem(NetworkDelivery delivery, int writerSize, Allocator writerAllocator, int maxWriterSize = -1) + { + Writer = new FastBufferWriter(writerSize, writerAllocator, maxWriterSize); + NetworkDelivery = delivery; + BatchHeader = default; + } + } + + internal delegate void MessageHandler(ref FastBufferReader reader, NetworkContext context); +#endregion + +#region Private Members + private DynamicUnmanagedArray m_IncomingMessageQueue = new DynamicUnmanagedArray(16); + + private MessageHandler[] m_MessageHandlers = new MessageHandler[255]; + private Type[] m_ReverseTypeMap = new Type[255]; + + private Dictionary m_MessageTypes = new Dictionary(); + private NativeHashMap>> m_SendQueues = new NativeHashMap>>(64, Allocator.Persistent); + + private List m_Hooks = new List(); + + private byte m_HighMessageType; + private object m_Owner; + private IMessageSender m_MessageSender; + private ulong m_LocalClientId; + private bool m_Disposed; +#endregion + + internal Type[] MessageTypes => m_ReverseTypeMap; + internal MessageHandler[] MessageHandlers => m_MessageHandlers; + internal int MessageHandlerCount => m_HighMessageType; + + internal byte GetMessageType(Type t) + { + return m_MessageTypes[t]; + } + + public MessagingSystem(IMessageSender messageSender, object owner, ulong localClientId = Int64.MaxValue) + { + try + { + m_LocalClientId = localClientId; + m_MessageSender = messageSender; + m_Owner = owner; + + var interfaceType = typeof(INetworkMessage); + var implementationTypes = new List(); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + foreach (var type in assembly.GetTypes()) + { + if (type.IsInterface || type.IsAbstract) + { + continue; + } + + if (interfaceType.IsAssignableFrom(type)) + { + var attributes = type.GetCustomAttributes(typeof(Bind), false); + // If [Bind(ownerType)] isn't provided, it defaults to being bound to NetworkManager + // This is technically a breach of domain by having MessagingSystem know about the existence + // of NetworkManager... but ultimately, Bind is provided to support testing, not to support + // general use of MessagingSystem outside of Netcode for GameObjects, so having MessagingSystem + // know about NetworkManager isn't so bad. Especially since it's just a default value. + // This is just a convenience to keep us and our users from having to use + // [Bind(typeof(NetworkManager))] on every message - only tests that don't want to use + // the full NetworkManager need to worry about it. + var allowedToBind = attributes.Length == 0 && m_Owner is NetworkManager; + for (var i = 0; i < attributes.Length; ++i) + { + Bind bindAttribute = (Bind) attributes[i]; + if ( + (bindAttribute.BoundType != null && + bindAttribute.BoundType.IsInstanceOfType(m_Owner)) || + (m_Owner == null && bindAttribute.BoundType == null)) + { + allowedToBind = true; + break; + } + } + + if (!allowedToBind) + { + continue; + } + + implementationTypes.Add(type); + } + } + } + + implementationTypes.Sort((a, b) => String.CompareOrdinal(a.FullName, b.FullName)); + foreach(var type in implementationTypes) + { + RegisterMessageType(type); + } + } + catch(Exception e) + { + Dispose(); + throw; + } + } + + public void Dispose() + { + if (m_Disposed) + { + return; + } + foreach (var queue in m_SendQueues) + { + queue.Value.Value.Dispose(); + } + m_SendQueues.Dispose(); + m_IncomingMessageQueue.Dispose(); + m_Disposed = true; + } + + ~MessagingSystem() + { + Dispose(); + } + + public void SetLocalClientId(ulong localClientId) + { + m_LocalClientId = localClientId; + } + + public void Hook(INetworkHooks hooks) + { + m_Hooks.Add(hooks); + } + + private void RegisterMessageType(Type messageType) + { + if (!typeof(INetworkMessage).IsAssignableFrom(messageType)) + { + throw new ArgumentException("RegisterMessageType types must be INetworkMessage types."); + } + + var method = messageType.GetMethod("Receive"); + if (method == null) + { + throw new InvalidMessageStructureException( + "All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); + } + + var asDelegate = Delegate.CreateDelegate(typeof(MessageHandler), method, false); + if (asDelegate == null) + { + throw new InvalidMessageStructureException( + "All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); + } + + m_MessageHandlers[m_HighMessageType] = (MessageHandler) asDelegate; + m_ReverseTypeMap[m_HighMessageType] = messageType; + m_MessageTypes[messageType] = m_HighMessageType++; + } + + internal void HandleIncomingData(ulong clientId, ArraySegment data, float receiveTime) + { + BatchHeader header; + unsafe + { + fixed (byte* nativeData = data.Array) + { + FastBufferReader batchReader = + new FastBufferReader(nativeData, Allocator.None, data.Count, data.Offset); + if (!batchReader.TryBeginRead(sizeof(BatchHeader))) + { + NetworkLog.LogWarning("Received a packet too small to contain a BatchHeader. Ignoring it."); + return; + } + batchReader.ReadValue(out header); + + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnReceiveBatch(clientId, header.BatchSize, batchReader.Length); + } + + for (var messageIdx = 0; messageIdx < header.BatchSize; ++messageIdx) + { + if (!batchReader.TryBeginRead(sizeof(MessageHeader))) + { + NetworkLog.LogWarning("Received a batch that didn't have enough data for all of its batches, ending early!"); + return; + } + batchReader.ReadValue(out MessageHeader messageHeader); + if (!batchReader.TryBeginRead(messageHeader.MessageSize)) + { + NetworkLog.LogWarning("Received a message that claimed a size larger than the packet, ending early!"); + return; + } + m_IncomingMessageQueue.Add(new ReceiveQueueItem + { + Header = messageHeader, + SenderId = clientId, + Timestamp = receiveTime, + // Copy the data for this message into a new FastBufferReader that owns that memory. + // We can't guarantee the memory in the ArraySegment stays valid because we don't own it, + // so we must move it to memory we do own. + Reader = new FastBufferReader(batchReader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, messageHeader.MessageSize) + }); + batchReader.Seek(batchReader.Position + messageHeader.MessageSize); + } + } + } + } + + public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, float timestamp) + { + var context = new NetworkContext + { + SystemOwner = m_Owner, + SenderId = senderId, + ReceivingChannel = header.NetworkChannel, + Timestamp = timestamp, + Header = header + }; + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnReceiveMessage(senderId, m_ReverseTypeMap[header.MessageType], header.NetworkChannel); + } + var handler = m_MessageHandlers[header.MessageType]; + using (reader) + { + handler.Invoke(ref reader, context); + } + } + + internal void ProcessIncomingMessageQueue() + { + for (var i = 0; i < m_IncomingMessageQueue.Count; ++i) + { + // Avoid copies... + ref var item = ref m_IncomingMessageQueue.GetValueRef(i); + HandleMessage(item.Header, ref item.Reader, item.SenderId, item.Timestamp); + } + + m_IncomingMessageQueue.Clear(); + } + + internal void ClientConnected(ulong clientId) + { + m_SendQueues[clientId] = DynamicUnmanagedArray.CreateRef(); + } + + internal void ClientDisconnected(ulong clientId) + { + var queue = m_SendQueues[clientId]; + for (var i = 0; i < queue.Value.Count; ++i) + { + queue.Value.GetValueRef(i).Writer.Dispose(); + } + queue.Value.Dispose(); + + m_SendQueues.Remove(clientId); + DynamicUnmanagedArray.ReleaseRef(queue); + } + + internal unsafe void SendMessage(in T message, NetworkChannel channel, NetworkDelivery delivery, in U clients) + where T: INetworkMessage + where U: IReadOnlyList + { + var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? 64000 : 1300; + var tmpSerializer = new FastBufferWriter(1300, Allocator.Temp, maxSize); + using (tmpSerializer) + { + message.Serialize(ref tmpSerializer); + + for (var i = 0; i < clients.Count; ++i) + { + var clientId = clients[i]; + + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnSendMessage(clientId, typeof(T), channel, delivery); + } + + ref var sendQueueItem = ref m_SendQueues[clientId].Value; + if (sendQueueItem.Count == 0) + { + sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, + maxSize)); + sendQueueItem.GetValueRef(0).Writer.Seek(sizeof(BatchHeader)); + } + else + { + ref var lastQueueItem = ref sendQueueItem.GetValueRef(sendQueueItem.Count - 1); + if (lastQueueItem.NetworkDelivery != delivery || + lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position < tmpSerializer.Length) + { + sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, + maxSize)); + sendQueueItem.GetValueRef(0).Writer.Seek(sizeof(BatchHeader)); + } + } + + ref var writeQueueItem = ref sendQueueItem.GetValueRef(sendQueueItem.Count - 1); + writeQueueItem.Writer.TryBeginWrite(sizeof(MessageHeader) + tmpSerializer.Length); + MessageHeader header = new MessageHeader + { + MessageSize = (short) tmpSerializer.Length, + MessageType = m_MessageTypes[typeof(T)], + NetworkChannel = channel + }; + + + if (clientId == m_LocalClientId) + { + m_IncomingMessageQueue.Add(new ReceiveQueueItem + { + Header = header, + Reader = new FastBufferReader(ref tmpSerializer, Allocator.TempJob), + SenderId = clientId, + Timestamp = Time.realtimeSinceStartup + }); + continue; + } + + writeQueueItem.Writer.WriteValue(header); + writeQueueItem.Writer.WriteBytes(tmpSerializer.GetUnsafePtr(), tmpSerializer.Length); + writeQueueItem.BatchHeader.BatchSize++; + } + } + } + + private struct PointerListWrapper : IReadOnlyList + where T : unmanaged + { + private unsafe T* m_Value; + private int m_Length; + + internal unsafe PointerListWrapper(T* ptr, int length) + { + m_Value = ptr; + m_Length = length; + } + + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Length; + } + + public unsafe T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Value[index]; + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal unsafe void SendMessage(in T message, NetworkChannel channel, NetworkDelivery delivery, + ulong* clientIds, int numClientIds) + where T: INetworkMessage + { + SendMessage(message, channel, delivery, new PointerListWrapper(clientIds, numClientIds)); + } + + internal void ProcessSendQueues() + { + foreach (var kvp in m_SendQueues) + { + var clientId = kvp.Key; + ref var sendQueueItem = ref kvp.Value.Value; + for (var i = 0; i < sendQueueItem.Count; ++i) + { + ref var queueItem = ref sendQueueItem.GetValueRef(i); + if (queueItem.BatchHeader.BatchSize == 0) + { + queueItem.Writer.Dispose(); + continue; + } + + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); + } + + queueItem.Writer.Seek(0); + // Skipping the Verify and sneaking the write mark in because we know it's fine. + queueItem.Writer.AllowedWriteMark = 2; + queueItem.Writer.WriteValue(queueItem.BatchHeader); + + m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); + queueItem.Writer.Dispose(); + } + sendQueueItem.Clear(); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs.meta new file mode 100644 index 0000000000..2aa51a8da4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a6de3c592caa3a41bdfe9b1e818bcf4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs new file mode 100644 index 0000000000..0434de5cca --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs @@ -0,0 +1,11 @@ +namespace Unity.Netcode +{ + public ref struct NetworkContext + { + public object SystemOwner; + public ulong SenderId; + public NetworkChannel ReceivingChannel; + public float Timestamp; + public MessageHeader Header; + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs.meta new file mode 100644 index 0000000000..523510d13f --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9a4d028b61e2b140927b5ebee04d384 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs new file mode 100644 index 0000000000..e3bc26a833 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs @@ -0,0 +1,49 @@ +using System; + +namespace Unity.Netcode +{ + public class NetworkManagerMessageSender : IMessageSender + { + private NetworkManager m_NetworkManager; + + public NetworkManagerMessageSender(NetworkManager manager) + { + m_NetworkManager = manager; + } + + public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) + { + + var length = batchData.Length; + //TODO: Transport needs to have a way to send it data without copying and allocating here. + var bytes = batchData.ToArray(); + var sendBuffer = new ArraySegment(bytes, 0, length); + + //TODO: Transport needs to accept sends by NetworkDelivery instead of NetworkChannel + NetworkChannel channel; + switch (delivery) + { + case NetworkDelivery.Reliable: + channel = NetworkChannel.DefaultMessage; + break; + case NetworkDelivery.Unreliable: + channel = NetworkChannel.SnapshotExchange; + break; + case NetworkDelivery.ReliableSequenced: + channel = NetworkChannel.Internal; + break; + case NetworkDelivery.UnreliableSequenced: + channel = NetworkChannel.UnreliableRpc; + break; + case NetworkDelivery.ReliableFragmentedSequenced: + channel = NetworkChannel.Fragmented; + break; + default: + channel = NetworkChannel.DefaultMessage; + break; + } + m_NetworkManager.NetworkConfig.NetworkTransport.Send(clientId, sendBuffer, channel); + + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta new file mode 100644 index 0000000000..da8bf169f6 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c90016576745774a945081ec90f87f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs new file mode 100644 index 0000000000..351e8f8109 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Netcode +{ + public struct DynamicUnmanagedArray : IReadOnlyList, IDisposable where T : unmanaged + { + private unsafe T* m_Data; + private Allocator m_Allocator; + private int m_Length; + private int m_Capacity; + + public int Count => m_Length; + + private int Capacity => m_Capacity; + + public bool IsReadOnly => false; + + public unsafe DynamicUnmanagedArray(int capacity, Allocator allocator = Allocator.Persistent) + { + m_Data = (T*)UnsafeUtility.Malloc(capacity * sizeof(T), UnsafeUtility.AlignOf(), allocator); + m_Allocator = allocator; + m_Length = 0; + m_Capacity = capacity; + } + + public unsafe void Dispose() + { + UnsafeUtility.Free(m_Data, m_Allocator); + } + + public unsafe T this[int index] + { + get => m_Data[index]; + set => m_Data[index] = value; + } + + public unsafe ref T GetValueRef(int index) + { + return ref m_Data[index]; + } + + private unsafe void Resize() + { + m_Capacity *= 2; + var data = (T*)UnsafeUtility.Malloc(m_Capacity * sizeof(T), UnsafeUtility.AlignOf(), m_Allocator); + UnsafeUtility.MemCpy(data, m_Data, m_Length); + UnsafeUtility.Free(m_Data, m_Allocator); + m_Data = data; + } + + public unsafe void Add(T item) + { + if (m_Length == m_Capacity) + { + Resize(); + } + m_Data[m_Length++] = item; + } + + public unsafe T Pop() + { + return m_Data[m_Length--]; + } + + public void Clear() + { + m_Length = 0; + } + + public IEnumerator GetEnumerator() + { + throw new System.NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public static unsafe Ref> CreateRef(int capacity = 16) + { + DynamicUnmanagedArray* array = + (DynamicUnmanagedArray*) UnsafeUtility.Malloc( + sizeof(DynamicUnmanagedArray), + UnsafeUtility.AlignOf>(), Allocator.Persistent); + + array->m_Data = (T*)UnsafeUtility.Malloc(capacity * sizeof(T), UnsafeUtility.AlignOf(), Allocator.Persistent); + array->m_Allocator = Allocator.Persistent; + array->m_Length = 0; + array->m_Capacity = capacity; + return new Ref>(array); + } + + public static unsafe void ReleaseRef(Ref> array) + { + array.Value.Dispose(); + UnsafeUtility.Free(array.Ptr, Allocator.Persistent); + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta new file mode 100644 index 0000000000..a069f33e73 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 521cff8a9643c6c4ab6d9ec0d4eb9a87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index 02572cbc46..5a627b28f9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -14,6 +14,11 @@ public unsafe Ref(ref T value) } } + public unsafe Ref(T* ptr) + { + m_Value = ptr; + } + public unsafe bool IsSet => m_Value != null; public unsafe ref T Value @@ -21,5 +26,11 @@ public unsafe ref T Value [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref *m_Value; } + + public unsafe T* Ptr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Value; + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/com.unity.netcode.runtime.asmdef b/com.unity.netcode.gameobjects/Runtime/com.unity.netcode.runtime.asmdef index 0edf4257ff..aa70cf68c5 100644 --- a/com.unity.netcode.gameobjects/Runtime/com.unity.netcode.runtime.asmdef +++ b/com.unity.netcode.gameobjects/Runtime/com.unity.netcode.runtime.asmdef @@ -4,7 +4,8 @@ "references": [ "Unity.Multiplayer.MetricTypes", "Unity.Multiplayer.NetStats", - "Unity.Multiplayer.NetStatsReporting" + "Unity.Multiplayer.NetStatsReporting", + "Unity.Collections" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging.meta new file mode 100644 index 0000000000..c03aaa7bb4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 55531000b0344935b665541f089df60e +timeCreated: 1630354914 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs new file mode 100644 index 0000000000..90f049a439 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.Internal; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Netcode.EditorTests +{ + public class DelayUtilityTests + { + [Bind(typeof(DelayUtilityTests))] + struct TestMessage : INetworkMessage + { + public int A; + public int B; + public int C; + public static bool Deserialized; + public static bool Delayed; + public static DelayUtility DelayUtility; + public static DelayUtility.DelayStage DelayStage; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValueSafe(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + if (DelayUtility.DelayUntil(DelayStage, ref reader, context) == DelayUtility.DelayResult.Delay) + { + Delayed = true; + return; + } + Deserialized = true; + reader.ReadValueSafe(out TestMessage value); + } + } + + private MessagingSystem m_MessagingSystem; + private DelayUtility m_DelayUtility; + private NetworkUpdateStage m_PreviousStage; + + [SetUp] + public void SetUp() + { + TestMessage.Deserialized = false; + TestMessage.Delayed = false; + + m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this); + m_DelayUtility = new DelayUtility(m_MessagingSystem); + TestMessage.DelayUtility = m_DelayUtility; + m_PreviousStage = NetworkUpdateLoop.UpdateStage; + } + + [TearDown] + public void TearDown() + { + m_MessagingSystem.Dispose(); + m_DelayUtility.Dispose(); + NetworkUpdateLoop.UpdateStage = m_PreviousStage; + } + + private TestMessage GetMessage() + { + var random = new Random(); + return new TestMessage + { + A = random.Next(), + B = random.Next(), + C = random.Next(), + }; + } + + [Test] + public void WhenDelayingALambda_TheLambdaIsCalledAtTheCorrectStage([Values]DelayUtility.DelayStage stage) + { + bool called = false; + m_DelayUtility.DelayUntil(stage, () => { called = true; }); + foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) + { + m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); + Assert.AreEqual((byte) networkStage == (byte) stage, called); + called = false; + } + } + + [Test] + public void AfterProcessingALambda_LambdaDoesNotGetProcessedAgain([Values]DelayUtility.DelayStage stage) + { + bool called = false; + m_DelayUtility.DelayUntil(stage, () => { called = true; }); + foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) + { + m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); + Assert.AreEqual((byte) networkStage == (byte) stage, called); + called = false; + } + foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) + { + m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); + Assert.IsFalse(called); + } + } + + [Test] + public void WhenDelayingAMessage_TheMessageIsProcessedAtTheCorrectStage([Values]DelayUtility.DelayStage stage) + { + TestMessage.DelayStage = stage; + var batchHeader = new BatchHeader + { + BatchSize = 1 + }; + var messageHeader = new MessageHeader + { + MessageSize = (short) UnsafeUtility.SizeOf(), + MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), + NetworkChannel = NetworkChannel.Internal + }; + var message = GetMessage(); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) + + FastBufferWriter.GetWriteSize(message)); + writer.WriteValue(batchHeader); + writer.WriteValue(messageHeader); + writer.WriteValue(message); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + NetworkUpdateLoop.UpdateStage = NetworkUpdateStage.Initialization; + using (reader) + { + m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); + m_MessagingSystem.ProcessIncomingMessageQueue(); + Assert.IsFalse(TestMessage.Deserialized); + Assert.IsTrue(TestMessage.Delayed); + } + + foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) + { + NetworkUpdateLoop.UpdateStage = (NetworkUpdateStage)networkStage; + m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); + Assert.AreEqual((byte) networkStage == (byte) stage, TestMessage.Deserialized); + TestMessage.Deserialized = false; + } + } + } + + [Test] + public void AfterProcessingAMessage_MessageDoesNotGetProcessedAgain([Values]DelayUtility.DelayStage stage) + { + TestMessage.DelayStage = stage; + var batchHeader = new BatchHeader + { + BatchSize = 1 + }; + var messageHeader = new MessageHeader + { + MessageSize = (short) UnsafeUtility.SizeOf(), + MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), + NetworkChannel = NetworkChannel.Internal + }; + var message = GetMessage(); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) + + FastBufferWriter.GetWriteSize(message)); + writer.WriteValue(batchHeader); + writer.WriteValue(messageHeader); + writer.WriteValue(message); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + NetworkUpdateLoop.UpdateStage = NetworkUpdateStage.Initialization; + using (reader) + { + m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); + m_MessagingSystem.ProcessIncomingMessageQueue(); + Assert.IsFalse(TestMessage.Deserialized); + Assert.IsTrue(TestMessage.Delayed); + } + + foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) + { + NetworkUpdateLoop.UpdateStage = (NetworkUpdateStage)networkStage; + m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); + Assert.AreEqual((byte) networkStage == (byte) stage, TestMessage.Deserialized); + TestMessage.Deserialized = false; + } + + foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) + { + NetworkUpdateLoop.UpdateStage = (NetworkUpdateStage)networkStage; + m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); + Assert.AreEqual(false, TestMessage.Deserialized); + } + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta new file mode 100644 index 0000000000..3c8940cd4e --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b0e1954601735a49985410a154cd856 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs new file mode 100644 index 0000000000..63f40d4af6 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.Internal; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Netcode.EditorTests +{ + public class MessageReceivingTests + { + [Bind(typeof(MessageReceivingTests))] + struct TestMessage : INetworkMessage + { + public int A; + public int B; + public int C; + public static bool Deserialized; + public static List DeserializedValues = new List(); + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValueSafe(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + Deserialized = true; + reader.ReadValueSafe(out TestMessage value); + DeserializedValues.Add(value); + } + } + + private MessagingSystem m_MessagingSystem; + + [SetUp] + public void SetUp() + { + TestMessage.Deserialized = false; + TestMessage.DeserializedValues.Clear(); + + m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this); + } + + [TearDown] + public void TearDown() + { + m_MessagingSystem.Dispose(); + } + + private TestMessage GetMessage() + { + var random = new Random(); + return new TestMessage + { + A = random.Next(), + B = random.Next(), + C = random.Next(), + }; + } + + [Test] + public void WhenHandlingAMessage_ReceiveMethodIsCalled() + { + var messageHeader = new MessageHeader + { + MessageSize = (short) UnsafeUtility.SizeOf(), + MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), + NetworkChannel = NetworkChannel.Internal + }; + var message = GetMessage(); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message)); + writer.WriteValue(message); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + m_MessagingSystem.HandleMessage(messageHeader, ref reader, 0, 0); + Assert.IsTrue(TestMessage.Deserialized); + Assert.AreEqual(1, TestMessage.DeserializedValues.Count); + Assert.AreEqual(message, TestMessage.DeserializedValues[0]); + } + } + } + + [Test] + public void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingMessageQueue() + { + var batchHeader = new BatchHeader + { + BatchSize = 1 + }; + var messageHeader = new MessageHeader + { + MessageSize = (short) UnsafeUtility.SizeOf(), + MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), + NetworkChannel = NetworkChannel.Internal + }; + var message = GetMessage(); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) + + FastBufferWriter.GetWriteSize(message)); + writer.WriteValue(batchHeader); + writer.WriteValue(messageHeader); + writer.WriteValue(message); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); + Assert.IsFalse(TestMessage.Deserialized); + Assert.IsEmpty(TestMessage.DeserializedValues);; + } + } + } + + [Test] + public void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled() + { + var batchHeader = new BatchHeader + { + BatchSize = 1 + }; + var messageHeader = new MessageHeader + { + MessageSize = (short) UnsafeUtility.SizeOf(), + MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), + NetworkChannel = NetworkChannel.Internal + }; + var message = GetMessage(); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) + + FastBufferWriter.GetWriteSize(message)); + writer.WriteValue(batchHeader); + writer.WriteValue(messageHeader); + writer.WriteValue(message); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); + m_MessagingSystem.ProcessIncomingMessageQueue(); + Assert.IsTrue(TestMessage.Deserialized); + Assert.AreEqual(1, TestMessage.DeserializedValues.Count); + Assert.AreEqual(message, TestMessage.DeserializedValues[0]); + } + } + } + + [Test] + public void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethodIsCalledMultipleTimes() + { + var batchHeader = new BatchHeader + { + BatchSize = 2 + }; + var messageHeader = new MessageHeader + { + MessageSize = (short) UnsafeUtility.SizeOf(), + MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), + NetworkChannel = NetworkChannel.Internal + }; + var message = GetMessage(); + var message2 = GetMessage(); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) * 2 + + FastBufferWriter.GetWriteSize(message) * 2); + writer.WriteValue(batchHeader); + writer.WriteValue(messageHeader); + writer.WriteValue(message); + writer.WriteValue(messageHeader); + writer.WriteValue(message2); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); + Assert.IsFalse(TestMessage.Deserialized); + Assert.IsEmpty(TestMessage.DeserializedValues); + + m_MessagingSystem.ProcessIncomingMessageQueue(); + Assert.IsTrue(TestMessage.Deserialized); + Assert.AreEqual(2, TestMessage.DeserializedValues.Count); + Assert.AreEqual(message, TestMessage.DeserializedValues[0]); + Assert.AreEqual(message2, TestMessage.DeserializedValues[1]); + } + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs.meta new file mode 100644 index 0000000000..45178e3b8d --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbc8fb6cf75f52d46a5d74971ce4b240 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs new file mode 100644 index 0000000000..79d084fd2e --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -0,0 +1,244 @@ + +using NUnit.Framework; +using Unity.Netcode.Transports.UNET; + +namespace Unity.Netcode.EditorTests +{ + public class MessageRegistrationTests + { + class MessagingSystemOwnerOne + { + + } + + class MessagingSystemOwnerTwo + { + + } + + [Bind(typeof(MessagingSystemOwnerOne))] + struct TestMessageOne : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + + } + } + + [Bind(typeof(MessagingSystemOwnerOne))] + struct TestMessageTwo : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + + } + } + + [Bind(typeof(MessagingSystemOwnerTwo))] + struct TestMessageThree : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + + } + } + + [Bind(null)] + struct TestMessageFour : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + + } + } + + [Test] + public void WhenCreatingMessageSystem_OnlyBoundTypesAreRegistered() + { + var ownerOne = new MessagingSystemOwnerOne(); + var ownerTwo = new MessagingSystemOwnerTwo(); + var sender = new NopMessageSender(); + + var systemOne = new MessagingSystem(sender, ownerOne); + var systemTwo = new MessagingSystem(sender, ownerTwo); + var systemThree = new MessagingSystem(sender, null); + + using (systemOne) + using (systemTwo) + using (systemThree) + { + Assert.AreEqual(2, systemOne.MessageHandlerCount); + Assert.AreEqual(1, systemTwo.MessageHandlerCount); + Assert.AreEqual(1, systemThree.MessageHandlerCount); + + Assert.Contains(typeof(TestMessageOne), systemOne.MessageTypes); + Assert.Contains(typeof(TestMessageTwo), systemOne.MessageTypes); + Assert.Contains(typeof(TestMessageThree), systemTwo.MessageTypes); + Assert.Contains(typeof(TestMessageFour), systemThree.MessageTypes); + } + } + + [Test] + public void WhenCreatingMessageSystem_BoundTypeMessageHandlersAreRegistered() + { + var ownerOne = new MessagingSystemOwnerOne(); + var ownerTwo = new MessagingSystemOwnerTwo(); + var sender = new NopMessageSender(); + + var systemOne = new MessagingSystem(sender, ownerOne); + var systemTwo = new MessagingSystem(sender, ownerTwo); + var systemThree = new MessagingSystem(sender, null); + + using (systemOne) + using (systemTwo) + using (systemThree) + { + MessagingSystem.MessageHandler handlerOne = TestMessageOne.Receive; + MessagingSystem.MessageHandler handlerTwo = TestMessageTwo.Receive; + MessagingSystem.MessageHandler handlerThree = TestMessageThree.Receive; + MessagingSystem.MessageHandler handlerFour = TestMessageFour.Receive; + + var foundHandlerOne = systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]; + + Assert.AreEqual(handlerOne, + systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]); + Assert.AreEqual(handlerTwo, + systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageTwo))]); + Assert.AreEqual(handlerThree, + systemTwo.MessageHandlers[systemTwo.GetMessageType(typeof(TestMessageThree))]); + Assert.AreEqual(handlerFour, + systemThree.MessageHandlers[systemThree.GetMessageType(typeof(TestMessageFour))]); + } + } + + #region WhenCreatingMessageSystem_MissingReceiveHandlerThrowsException + class BrokenSystemOwnerOne + { + + } + + [Bind(typeof(BrokenSystemOwnerOne))] + struct TestMessageFive : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + } + + [Test] + public void WhenCreatingMessageSystem_MissingReceiveHandlerThrowsException() + { + var owner = new BrokenSystemOwnerOne(); + var sender = new NopMessageSender(); + Assert.Throws(() => new MessagingSystem(sender, owner)); + } + #endregion + + #region WhenCreatingMessageSystem_ReceiveHandlerWithIncorrectParametersThrowsException + class BrokenSystemOwnerTwo + { + + } + + [Bind(typeof(BrokenSystemOwnerTwo))] + struct TestMessageSix : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(ref FastBufferReader reader) + { + + } + } + + [Test] + public void WhenCreatingMessageSystem_ReceiveHandlerWithIncorrectParametersThrowsException() + { + var owner = new BrokenSystemOwnerTwo(); + var sender = new NopMessageSender(); + Assert.Throws(() => new MessagingSystem(sender, owner)); + } + #endregion + + #region WhenCreatingMessageSystem_ReceiveHandlerWithMissingRefSpecifierThrowsException + class BrokenSystemOwnerThree + { + + } + + [Bind(typeof(BrokenSystemOwnerThree))] + struct TestMessageSeven : INetworkMessage + { + public int A; + public int B; + public int C; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(FastBufferReader reader, NetworkContext context) + { + + } + } + + [Test] + public void WhenCreatingMessageSystem_ReceiveHandlerWithMissingRefSpecifierThrowsException() + { + var owner = new BrokenSystemOwnerThree(); + var sender = new NopMessageSender(); + Assert.Throws(() => new MessagingSystem(sender, owner)); + } + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs.meta new file mode 100644 index 0000000000..860ab64387 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03e7cc2b6f0c5c540a529429f48529f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs new file mode 100644 index 0000000000..74fc2771f9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Netcode.EditorTests +{ + public class MessageSendingTests + { + [Bind(typeof(MessageSendingTests))] + struct TestMessage : INetworkMessage + { + public int A; + public int B; + public int C; + public static bool Serialized; + + public void Serialize(ref FastBufferWriter writer) + { + Serialized = true; + writer.WriteValueSafe(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + } + } + + class TestMessageSender : IMessageSender + { + public List MessageQueue = new List(); + + public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) + { + MessageQueue.Add(batchData.ToArray()); + } + } + + private TestMessageSender m_MessageSender; + private MessagingSystem m_MessagingSystem; + private ulong[] m_Clients = {0}; + + [SetUp] + public void SetUp() + { + TestMessage.Serialized = false; + + m_MessageSender = new TestMessageSender(); + m_MessagingSystem = new MessagingSystem(m_MessageSender, this); + m_MessagingSystem.ClientConnected(0); + } + + [TearDown] + public void TearDown() + { + m_MessagingSystem.Dispose(); + } + + private TestMessage GetMessage() + { + var random = new Random(); + return new TestMessage + { + A = random.Next(), + B = random.Next(), + C = random.Next(), + }; + } + + [Test] + public void WhenSendingMessage_SerializeIsCalled() + { + var message = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + Assert.IsTrue(TestMessage.Serialized); + } + + [Test] + public void WhenSendingMessage_NothingIsSentBeforeProcessingSendQueue() + { + var message = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + Assert.IsEmpty(m_MessageSender.MessageQueue); + } + + [Test] + public void WhenProcessingSendQueue_MessageIsSent() + { + var message = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenSendingMultipleMessages_MessagesAreBatched() + { + var message = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenNotExceedingBatchSize_NewBatchesAreNotCreated() + { + var message = GetMessage(); + var size = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf(); + for (var i = 0; i < 1300 / size; ++i) + { + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + } + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenExceedingBatchSize_NewBatchesAreCreated() + { + var message = GetMessage(); + var size = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf(); + for (var i = 0; i < (1300 / size) + 1; ++i) + { + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + } + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(2, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated() + { + var message = GetMessage(); + var size = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf(); + for (var i = 0; i < (1300 / size) + 1; ++i) + { + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.ReliableFragmentedSequenced, m_Clients); + } + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenSwitchingDelivery_NewBatchesAreCreated() + { + var message = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Unreliable, m_Clients); + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(2, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenSwitchingChannel_NewBatchesAreNotCreated() + { + var message = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkChannel.ReliableRpc, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + + m_MessagingSystem.ProcessSendQueues(); + Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); + } + + [Test] + public void WhenSendingMessaged_SentDataIsCorrect() + { + var message = GetMessage(); + var message2 = GetMessage(); + m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message2, NetworkChannel.ReliableRpc, NetworkDelivery.Reliable, m_Clients); + + m_MessagingSystem.ProcessSendQueues(); + var reader = new FastBufferReader(m_MessageSender.MessageQueue[0], Allocator.Temp); + using (reader) + { + reader.TryBeginRead( + FastBufferWriter.GetWriteSize() + + FastBufferWriter.GetWriteSize() * 2 + + FastBufferWriter.GetWriteSize() * 2 + ); + reader.ReadValue(out BatchHeader header); + Assert.AreEqual(2, header.BatchSize); + + reader.ReadValue(out MessageHeader messageHeader); + Assert.AreEqual(NetworkChannel.Internal, messageHeader.NetworkChannel); + Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader.MessageType); + Assert.AreEqual(UnsafeUtility.SizeOf(), messageHeader.MessageSize); + reader.ReadValue(out TestMessage receivedMessage); + Assert.AreEqual(message, receivedMessage); + + reader.ReadValue(out MessageHeader messageHeader2); + Assert.AreEqual(NetworkChannel.ReliableRpc, messageHeader2.NetworkChannel); + Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader2.MessageType); + Assert.AreEqual(UnsafeUtility.SizeOf(), messageHeader2.MessageSize); + reader.ReadValue(out TestMessage receivedMessage2); + Assert.AreEqual(message2, receivedMessage2); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs.meta new file mode 100644 index 0000000000..858913264b --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ea2fc218c5e07c54795fc9bed4a6a62c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs new file mode 100644 index 0000000000..64081420d3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs @@ -0,0 +1,9 @@ +namespace Unity.Netcode.EditorTests +{ + class NopMessageSender : IMessageSender + { + public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) + { + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs.meta new file mode 100644 index 0000000000..321f2a7771 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 817c58672ba39a74da57082ed176956e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 81c3dc36834801fba025284aaae765ed3b3819e7 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 1 Sep 2021 11:12:48 -0500 Subject: [PATCH 25/58] Killed DelayUtil. --- .../Runtime/Messaging/DelayUtility.cs | 120 ---------- .../Runtime/Messaging/DelayUtility.cs.meta | 11 - .../Editor/Messaging/DelayUtilityTests.cs | 205 ------------------ .../Messaging/DelayUtilityTests.cs.meta | 11 - 4 files changed, 347 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs deleted file mode 100644 index 3930747356..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections.Generic; -using Unity.Collections; - -namespace Unity.Netcode -{ - public class DelayUtility: IDisposable - { - public enum DelayStage: byte - { - PreFixedUpdate = NetworkUpdateStage.FixedUpdate, - PreUpdate = NetworkUpdateStage.Update, - PreLateUpdate = NetworkUpdateStage.PreLateUpdate, - PostLateUpdate = NetworkUpdateStage.PostLateUpdate - } - - public delegate void DelayedNetworkAction(); - - private Dictionary> m_DelayedCallbacks = new Dictionary> - { - [DelayStage.PreFixedUpdate] = new List(), - [DelayStage.PreUpdate] = new List(), - [DelayStage.PreLateUpdate] = new List(), - [DelayStage.PostLateUpdate] = new List(), - }; - - private struct DelayedMessageHandlerData - { - public FastBufferReader Reader; - public MessageHeader Header; - public ulong SenderId; - public float Timestamp; - } - - private NativeHashMap>> m_DelayedMessageHandlers = - new NativeHashMap>>((int)DelayStage.PostLateUpdate, Allocator.Persistent); - - private IMessageHandler m_MessageHandler; - private bool m_disposed; - - public DelayUtility(IMessageHandler messageHandler) - { - m_MessageHandler = messageHandler; - m_DelayedMessageHandlers[(byte) DelayStage.PreFixedUpdate] = - DynamicUnmanagedArray.CreateRef(); - m_DelayedMessageHandlers[(byte) DelayStage.PreUpdate] = - DynamicUnmanagedArray.CreateRef(); - m_DelayedMessageHandlers[(byte) DelayStage.PreLateUpdate] = - DynamicUnmanagedArray.CreateRef(); - m_DelayedMessageHandlers[(byte) DelayStage.PostLateUpdate] = - DynamicUnmanagedArray.CreateRef(); - } - - public void Dispose() - { - if (!m_disposed) - { - foreach (var handler in m_DelayedMessageHandlers) - { - handler.Value.Value.Dispose(); - } - m_DelayedMessageHandlers.Dispose(); - m_disposed = true; - } - } - - ~DelayUtility() - { - Dispose(); - } - - public void DelayUntil(DelayStage stage, DelayedNetworkAction callback) - { - m_DelayedCallbacks[stage].Add(callback); - } - - public enum DelayResult - { - Delay, - Continue - } - - - public unsafe DelayResult DelayUntil(DelayStage stage, ref FastBufferReader reader, NetworkContext context) - { - if ((NetworkUpdateStage)stage == NetworkUpdateLoop.UpdateStage) - { - return DelayResult.Continue; - } - m_DelayedMessageHandlers[(byte)stage].Value.Add(new DelayedMessageHandlerData - { - // Have to make a copy so we can manage the lifetime. - Reader = new FastBufferReader(reader.GetUnsafePtr(), Allocator.TempJob, reader.Length), - Header = context.Header, - SenderId = context.SenderId, - Timestamp = context.Timestamp - }); - return DelayResult.Delay; - } - - public void NetworkUpdate(NetworkUpdateStage stage) - { - ref var delayedMessageHandlers = ref m_DelayedMessageHandlers[(byte) stage].Value; - for (var i = 0; i < delayedMessageHandlers.Count; ++i) - { - ref var data = ref delayedMessageHandlers.GetValueRef(i); - m_MessageHandler.HandleMessage(data.Header, ref data.Reader, data.SenderId, data.Timestamp); - } - delayedMessageHandlers.Clear(); - - var delayedCallbacks = m_DelayedCallbacks[(DelayStage) stage]; - for (var i = 0; i < delayedCallbacks.Count; ++i) - { - delayedCallbacks[i].Invoke(); - } - delayedCallbacks.Clear(); - } - - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta deleted file mode 100644 index a3b3706ec4..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DelayUtility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 69b372fb7b437f548afc8a419933dab6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs deleted file mode 100644 index 90f049a439..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using NUnit.Framework.Internal; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Netcode.EditorTests -{ - public class DelayUtilityTests - { - [Bind(typeof(DelayUtilityTests))] - struct TestMessage : INetworkMessage - { - public int A; - public int B; - public int C; - public static bool Deserialized; - public static bool Delayed; - public static DelayUtility DelayUtility; - public static DelayUtility.DelayStage DelayStage; - - public void Serialize(ref FastBufferWriter writer) - { - writer.WriteValueSafe(this); - } - - public static void Receive(ref FastBufferReader reader, NetworkContext context) - { - if (DelayUtility.DelayUntil(DelayStage, ref reader, context) == DelayUtility.DelayResult.Delay) - { - Delayed = true; - return; - } - Deserialized = true; - reader.ReadValueSafe(out TestMessage value); - } - } - - private MessagingSystem m_MessagingSystem; - private DelayUtility m_DelayUtility; - private NetworkUpdateStage m_PreviousStage; - - [SetUp] - public void SetUp() - { - TestMessage.Deserialized = false; - TestMessage.Delayed = false; - - m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this); - m_DelayUtility = new DelayUtility(m_MessagingSystem); - TestMessage.DelayUtility = m_DelayUtility; - m_PreviousStage = NetworkUpdateLoop.UpdateStage; - } - - [TearDown] - public void TearDown() - { - m_MessagingSystem.Dispose(); - m_DelayUtility.Dispose(); - NetworkUpdateLoop.UpdateStage = m_PreviousStage; - } - - private TestMessage GetMessage() - { - var random = new Random(); - return new TestMessage - { - A = random.Next(), - B = random.Next(), - C = random.Next(), - }; - } - - [Test] - public void WhenDelayingALambda_TheLambdaIsCalledAtTheCorrectStage([Values]DelayUtility.DelayStage stage) - { - bool called = false; - m_DelayUtility.DelayUntil(stage, () => { called = true; }); - foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) - { - m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); - Assert.AreEqual((byte) networkStage == (byte) stage, called); - called = false; - } - } - - [Test] - public void AfterProcessingALambda_LambdaDoesNotGetProcessedAgain([Values]DelayUtility.DelayStage stage) - { - bool called = false; - m_DelayUtility.DelayUntil(stage, () => { called = true; }); - foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) - { - m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); - Assert.AreEqual((byte) networkStage == (byte) stage, called); - called = false; - } - foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) - { - m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); - Assert.IsFalse(called); - } - } - - [Test] - public void WhenDelayingAMessage_TheMessageIsProcessedAtTheCorrectStage([Values]DelayUtility.DelayStage stage) - { - TestMessage.DelayStage = stage; - var batchHeader = new BatchHeader - { - BatchSize = 1 - }; - var messageHeader = new MessageHeader - { - MessageSize = (short) UnsafeUtility.SizeOf(), - MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), - NetworkChannel = NetworkChannel.Internal - }; - var message = GetMessage(); - - var writer = new FastBufferWriter(1300, Allocator.Temp); - using (writer) - { - writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + - FastBufferWriter.GetWriteSize(messageHeader) + - FastBufferWriter.GetWriteSize(message)); - writer.WriteValue(batchHeader); - writer.WriteValue(messageHeader); - writer.WriteValue(message); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - NetworkUpdateLoop.UpdateStage = NetworkUpdateStage.Initialization; - using (reader) - { - m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); - m_MessagingSystem.ProcessIncomingMessageQueue(); - Assert.IsFalse(TestMessage.Deserialized); - Assert.IsTrue(TestMessage.Delayed); - } - - foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) - { - NetworkUpdateLoop.UpdateStage = (NetworkUpdateStage)networkStage; - m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); - Assert.AreEqual((byte) networkStage == (byte) stage, TestMessage.Deserialized); - TestMessage.Deserialized = false; - } - } - } - - [Test] - public void AfterProcessingAMessage_MessageDoesNotGetProcessedAgain([Values]DelayUtility.DelayStage stage) - { - TestMessage.DelayStage = stage; - var batchHeader = new BatchHeader - { - BatchSize = 1 - }; - var messageHeader = new MessageHeader - { - MessageSize = (short) UnsafeUtility.SizeOf(), - MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), - NetworkChannel = NetworkChannel.Internal - }; - var message = GetMessage(); - - var writer = new FastBufferWriter(1300, Allocator.Temp); - using (writer) - { - writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + - FastBufferWriter.GetWriteSize(messageHeader) + - FastBufferWriter.GetWriteSize(message)); - writer.WriteValue(batchHeader); - writer.WriteValue(messageHeader); - writer.WriteValue(message); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - NetworkUpdateLoop.UpdateStage = NetworkUpdateStage.Initialization; - using (reader) - { - m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); - m_MessagingSystem.ProcessIncomingMessageQueue(); - Assert.IsFalse(TestMessage.Deserialized); - Assert.IsTrue(TestMessage.Delayed); - } - - foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) - { - NetworkUpdateLoop.UpdateStage = (NetworkUpdateStage)networkStage; - m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); - Assert.AreEqual((byte) networkStage == (byte) stage, TestMessage.Deserialized); - TestMessage.Deserialized = false; - } - - foreach (var networkStage in Enum.GetValues(typeof(DelayUtility.DelayStage))) - { - NetworkUpdateLoop.UpdateStage = (NetworkUpdateStage)networkStage; - m_DelayUtility.NetworkUpdate((NetworkUpdateStage)networkStage); - Assert.AreEqual(false, TestMessage.Deserialized); - } - } - } - } -} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta deleted file mode 100644 index 3c8940cd4e..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DelayUtilityTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3b0e1954601735a49985410a154cd856 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From b24d1a78b313bcdda984fc6bba889b37040fd89c Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 7 Sep 2021 21:14:09 -0500 Subject: [PATCH 26/58] Hooked up MessagingSystem and converted ConnectionRequestMessage and ConnectionApprovedMessage. A couple of tests are known to be failing... this is to let the work start being divided up. --- .../Runtime/Core/NetworkBehaviour.cs | 98 +++++++ .../Runtime/Core/NetworkManager.cs | 198 ++++++++++--- .../Runtime/Core/NetworkObject.cs | 264 ++++++++++++++++++ .../Messaging/IInternalMessageHandler.cs | 2 - .../Runtime/Messaging/INetworkHooks.cs | 16 +- .../Messaging/InternalMessageHandler.cs | 74 +---- .../Runtime/Messaging/MessageBatcher.cs | 4 + .../Runtime/Messaging/MessageHeader.cs | 1 - .../MessageQueue/MessageQueueContainer.cs | 2 - .../MessageQueue/MessageQueueProcessor.cs | 14 - .../Runtime/Messaging/Messages.meta | 8 + .../Messages/ConnectionApprovedMessage.cs | 85 ++++++ .../ConnectionApprovedMessage.cs.meta} | 2 +- .../Messages/ConnectionRequestMessage.cs | 145 ++++++++++ .../Messages/ConnectionRequestMessage.cs.meta | 11 + .../Runtime/Messaging/MessagingSystem.cs | 121 ++++++-- .../Runtime/Messaging/NetworkContext.cs | 1 - .../Messaging/NetworkManagerMessageSender.cs | 49 ---- .../Collections/NetworkDictionary.cs | 212 +++++++++++++- .../Collections/NetworkList.cs | 241 +++++++++++++++- .../NetworkVariable/Collections/NetworkSet.cs | 138 ++++++++- .../NetworkVariable/INetworkVariable.cs | 105 +++++++ .../NetworkVariable/INetworkVariable.cs.meta | 11 + .../NetworkVariable/NetworkVariable.cs | 39 +++ .../NetworkVariable/NetworkVariableBase.cs | 25 ++ ...nternalMessageHandlerProfilingDecorator.cs | 20 -- .../Runtime/Serialization/FastBufferReader.cs | 79 ++++++ .../Runtime/Serialization/FastBufferWriter.cs | 112 +++++++- .../Runtime/Spawning/NetworkSpawnManager.cs | 102 +++++++ .../Runtime/Utility/DynamicUnmanagedArray.cs | 3 +- .../Runtime/Utility/FixedUnmanagedArray.cs | 183 ++++++++++++ .../Utility/FixedUnmanagedArray.cs.meta | 11 + .../Tests/Editor/DummyMessageHandler.cs | 4 - .../Editor/Messaging/MessageReceivingTests.cs | 4 - .../Editor/Messaging/MessageSendingTests.cs | 36 ++- .../NetworkManagerMessageHandlerTests.cs | 26 -- ...alMessageHandlerProfilingDecoratorTests.cs | 16 -- .../BaseFastBufferReaderWriterTest.cs | 4 +- .../Serialization/BufferSerializerTests.cs | 4 +- .../com.unity.netcode.editortests.asmdef | 2 - .../Tests/Runtime/NetworkVarBufferCopyTest.cs | 48 ++++ testproject/Assets/Resources.meta | 8 + .../Resources/PerformanceTestRunInfo.json | 79 ++++++ .../PerformanceTestRunInfo.json.meta | 7 + .../Runtime/MultiClientConnectionApproval.cs | 2 +- 45 files changed, 2312 insertions(+), 304 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs rename com.unity.netcode.gameobjects/Runtime/Messaging/{NetworkManagerMessageSender.cs.meta => Messages/ConnectionApprovedMessage.cs.meta} (83%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta create mode 100644 testproject/Assets/Resources.meta create mode 100644 testproject/Assets/Resources/PerformanceTestRunInfo.json create mode 100644 testproject/Assets/Resources/PerformanceTestRunInfo.json.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 9c25263d21..3dc45f01a8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Linq; using System.IO; +using Unity.Collections; namespace Unity.Netcode { @@ -752,6 +753,47 @@ internal void WriteNetworkVariableData(Stream stream, ulong clientId) } } + internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong clientId) + { + if (NetworkVariableFields.Count == 0) + { + return; + } + + for (int j = 0; j < NetworkVariableFields.Count; j++) + { + bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId); + + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + if (!canClientRead) + { + writer.WriteValue((ushort)0); + } + } + else + { + writer.WriteValue(canClientRead); + } + + if (canClientRead) + { + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + var tmpWriter = new FastBufferWriter(1300, Allocator.Temp, Int16.MaxValue); + NetworkVariableFields[j].WriteField(ref tmpWriter); + + writer.WriteValue((ushort)tmpWriter.Length); + tmpWriter.CopyTo(ref writer); + } + else + { + NetworkVariableFields[j].WriteField(ref writer); + } + } + } + } + internal void SetNetworkVariableData(Stream stream) { if (NetworkVariableFields.Count == 0) @@ -814,6 +856,62 @@ internal void SetNetworkVariableData(Stream stream) } } } + + internal void SetNetworkVariableData(ref FastBufferReader reader) + { + if (NetworkVariableFields.Count == 0) + { + return; + } + + for (int j = 0; j < NetworkVariableFields.Count; j++) + { + ushort varSize = 0; + + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + reader.ReadValue(out varSize); + + if (varSize == 0) + { + continue; + } + } + else + { + reader.ReadValue(out bool clientCanRead); + if (!clientCanRead) + { + continue; + } + } + + var readStartPos = reader.Position; + NetworkVariableFields[j].ReadField(ref reader); + + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + if (reader.Position > (readStartPos + varSize)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Var data read too far. {reader.Position - (readStartPos + varSize)} bytes."); + } + + reader.Seek(readStartPos + varSize); + } + else if (reader.Position < (readStartPos + varSize)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Var data read too little. {(readStartPos + varSize) - reader.Position} bytes."); + } + + reader.Seek(readStartPos + varSize); + } + } + } + } /// /// Gets the local instance of a object with a given NetworkId diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 6bbd2841ba..373299eb79 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -2,6 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Unity.Collections; +using Unity.Netcode.Messages; using UnityEngine; #if UNITY_EDITOR using UnityEditor; @@ -49,6 +51,8 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem internal SnapshotSystem SnapshotSystem { get; private set; } internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; } + private MessagingSystem m_MessagingSystem; + private NetworkPrefabHandler m_PrefabHandler; public NetworkPrefabHandler PrefabHandler @@ -64,6 +68,93 @@ public NetworkPrefabHandler PrefabHandler } } + private class NetworkManagerHooks : INetworkHooks + { + private NetworkManager m_NetworkManager; + + internal NetworkManagerHooks(NetworkManager manager) + { + m_NetworkManager = manager; + } + + public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) + { + } + + public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) + { + } + + public void OnBeforeReceiveMessage(ulong senderId, Type messageType) + { + } + + public void OnAfterReceiveMessage(ulong senderId, Type messageType) + { + } + + public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery) + { + } + + public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery) + { + } + + public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes) + { + } + + public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes) + { + } + + public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery) + { + return true; + } + + public bool OnVerifyCanReceive(ulong senderId, Type messageType) + { + if(m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) && + (client.ConnectionState == PendingClient.State.PendingApproval || + (client.ConnectionState == PendingClient.State.PendingConnection && + messageType != typeof(ConnectionRequestMessage)))) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId.ToString()} before it has been accepted"); + } + + return false; + } + + return true; + } + } + + private class NetworkManagerMessageSender : IMessageSender + { + private NetworkManager m_NetworkManager; + + public NetworkManagerMessageSender(NetworkManager manager) + { + m_NetworkManager = manager; + } + + public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) + { + + var length = batchData.Length; + //TODO: Transport needs to have a way to send it data without copying and allocating here. + var bytes = batchData.ToArray(); + var sendBuffer = new ArraySegment(bytes, 0, length); + + m_NetworkManager.NetworkConfig.NetworkTransport.Send(clientId, sendBuffer, delivery); + + } + } + /// /// Returns the to use as the override as could be defined within the NetworkPrefab list /// Note: This should be used to create pools (with components) @@ -148,7 +239,11 @@ public GameObject GetNetworkPrefabOverride(GameObject gameObject) public ulong LocalClientId { get => IsServer ? NetworkConfig.NetworkTransport.ServerClientId : m_LocalClientId; - internal set => m_LocalClientId = value; + internal set + { + m_LocalClientId = value; + m_MessagingSystem.SetLocalClientId(value); + } } private ulong m_LocalClientId; @@ -360,8 +455,13 @@ private void Initialize(bool server) // Cannot allow any new data to arrive from the wire after MessageQueueContainer's Initialization update // has run this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); + this.RegisterNetworkUpdate(NetworkUpdateStage.PostLateUpdate); + + m_MessagingSystem = new MessagingSystem(new NetworkManagerMessageSender(this), this, UInt64.MaxValue); + m_MessagingSystem.Hook(new NetworkManagerHooks(this)); - LocalClientId = 0; + LocalClientId = UInt64.MaxValue; + PendingClients.Clear(); ConnectedClients.Clear(); ConnectedClientsList.Clear(); @@ -936,6 +1036,12 @@ public void Shutdown() NetworkTickSystem.Tick -= OnNetworkManagerTick; NetworkTickSystem = null; } + + if (m_MessagingSystem != null) + { + m_MessagingSystem.Dispose(); + m_MessagingSystem = null; + } NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll; @@ -993,6 +1099,9 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) case NetworkUpdateStage.PreUpdate: OnNetworkPreUpdate(); break; + case NetworkUpdateStage.PostLateUpdate: + OnNetworkPostLateUpdate(); + break; } } @@ -1014,6 +1123,8 @@ private void OnNetworkEarlyUpdate() // Only do another iteration if: there are no more messages AND (there is no limit to max events or we have processed less than the maximum) } while (IsListening && networkEvent != NetworkEvent.Nothing); + m_MessagingSystem.ProcessIncomingMessageQueue(); + #if DEVELOPMENT_BUILD || UNITY_EDITOR s_TransportPoll.End(); #endif @@ -1032,6 +1143,11 @@ private void OnNetworkPreUpdate() } } + private void OnNetworkPostLateUpdate() + { + m_MessagingSystem.ProcessSendQueues(); + } + /// /// This function runs once whenever the local tick is incremented and is responsible for the following (in order): /// - collect commands/inputs and send them to the server (TBD) @@ -1057,18 +1173,15 @@ private void OnNetworkManagerTick() private void SendConnectionRequest() { - var clientIds = new[] { ServerClientId }; - var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ConnectionRequest, NetworkDelivery.ReliableSequenced, clientIds, NetworkUpdateStage.EarlyUpdate); - if (context != null) - { - using var nonNullContext = (InternalCommandContext)context; - nonNullContext.NetworkWriter.WriteUInt64Packed(NetworkConfig.GetConfig()); - - if (NetworkConfig.ConnectionApproval) - { - nonNullContext.NetworkWriter.WriteByteArray(NetworkConfig.ConnectionData); - } - } + var message = new ConnectionRequestMessage + { + ConfigHash = NetworkConfig.GetConfig(), + ShouldSendConnectionData = NetworkConfig.ConnectionApproval, + ConnectionData = + new FixedUnmanagedArray(NetworkConfig + .ConnectionData) + }; + SendMessage(message, NetworkDelivery.Reliable, ServerClientId); } private IEnumerator ApprovalTimeout(ulong clientId) @@ -1103,6 +1216,7 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A #if DEVELOPMENT_BUILD || UNITY_EDITOR s_TransportConnect.Begin(); #endif + m_MessagingSystem.ClientConnected(clientId); if (IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) @@ -1152,6 +1266,7 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A { NetworkLog.LogInfo($"Disconnect Event From {clientId}"); } + m_MessagingSystem.ClientDisconnected(clientId); if (IsServer) { @@ -1174,8 +1289,34 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A private readonly NetworkBuffer m_InputBufferWrapper = new NetworkBuffer(new byte[0]); private readonly MessageBatcher m_MessageBatcher = new MessageBatcher(); + public void SendMessage(in T message, NetworkDelivery delivery, in U clientIds) + where T : INetworkMessage + where U : IReadOnlyList + { + m_MessagingSystem.SendMessage(message, delivery, clientIds); + } + + public unsafe void SendMessage(in T message, NetworkDelivery delivery, + ulong* clientIds, int numClientIds) + where T: INetworkMessage + { + m_MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds); + } + + public void SendMessage(in T message, NetworkDelivery delivery, ulong clientId) + where T: INetworkMessage + { + m_MessagingSystem.SendMessage(message, delivery, clientId); + } + internal void HandleIncomingData(ulong clientId, ArraySegment payload, float receiveTime) { + var firstByte = payload.Array[0]; + if (firstByte != 0b11111111) + { + m_MessagingSystem.HandleIncomingData(clientId, payload, receiveTime); + return; + } #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleIncomingData.Begin(); #endif @@ -1187,7 +1328,7 @@ internal void HandleIncomingData(ulong clientId, ArraySegment payload, flo m_InputBufferWrapper.SetTarget(payload.Array); m_InputBufferWrapper.SetLength(payload.Count + payload.Offset); - m_InputBufferWrapper.Position = payload.Offset; + m_InputBufferWrapper.Position = payload.Offset + 1; using var messageStream = m_InputBufferWrapper; // Client tried to send a network message that was not the connection request before he was accepted. @@ -1427,29 +1568,22 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? // Server doesn't send itself the connection approved message if (ownerClientId != ServerClientId) { - var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ConnectionApproved, NetworkDelivery.ReliableSequenced, new[] { ownerClientId }, NetworkUpdateStage.EarlyUpdate); - if (context != null) + var message = new ConnectionApprovedMessage { - using var nonNullContext = (InternalCommandContext)context; - nonNullContext.NetworkWriter.WriteUInt64Packed(ownerClientId); - nonNullContext.NetworkWriter.WriteInt32Packed(LocalTime.Tick); - - // If scene management is disabled, then just serialize all client relative (observed) NetworkObjects - if (!NetworkConfig.EnableSceneManagement) - { - SpawnManager.SerializeObservedNetworkObjects(ownerClientId, nonNullContext.NetworkWriter); - } - } - - // If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization - if (NetworkConfig.EnableSceneManagement) + OwnerClientId = ownerClientId, + NetworkTick = LocalTime.Tick + }; + if (!NetworkConfig.EnableSceneManagement) { - SceneManager.SynchronizeNetworkObjects(ownerClientId); + SpawnManager.SerializeObservedNetworkObjects(ownerClientId, ref message); + InvokeOnClientConnectedCallback(ownerClientId); } + // If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization else { - InvokeOnClientConnectedCallback(ownerClientId); + SceneManager.SynchronizeNetworkObjects(ownerClientId); } + SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId); } else // Server just adds itself as an observer to all spawned NetworkObjects { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 3eb83190f9..04cf324f07 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -3,7 +3,10 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using NUnit.Framework; +using Unity.Collections; using UnityEngine; +using UnityEngine.Profiling.Memory.Experimental; namespace Unity.Netcode { @@ -852,6 +855,16 @@ internal void WriteNetworkVariableData(Stream stream, ulong clientId) } } + internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong clientId) + { + for (int i = 0; i < ChildNetworkBehaviours.Count; i++) + { + var behavior = ChildNetworkBehaviours[i]; + behavior.InitializeVariables(); + behavior.WriteNetworkVariableData(ref writer, clientId); + } + } + internal void SetNetworkVariableData(Stream stream) { for (int i = 0; i < ChildNetworkBehaviours.Count; i++) @@ -862,6 +875,16 @@ internal void SetNetworkVariableData(Stream stream) } } + internal void SetNetworkVariableData(ref FastBufferReader reader) + { + for (int i = 0; i < ChildNetworkBehaviours.Count; i++) + { + var behaviour = ChildNetworkBehaviours[i]; + behaviour.InitializeVariables(); + behaviour.SetNetworkVariableData(ref reader); + } + } + internal ushort GetNetworkBehaviourOrderIndex(NetworkBehaviour instance) { // read the cached index, and verify it first @@ -904,6 +927,209 @@ internal NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index) return ChildNetworkBehaviours[index]; } + internal struct SceneObject + { + public struct SceneObjectMetadata + { + public ulong NetworkObjectId; + public ulong OwnerClientId; + public uint Hash; + + public bool IsPlayerObject; + public bool HasParent; + public bool IsSceneObject; + public bool HasTransform; + public bool IsReparented; + public bool HasNetworkVariables; + } + + public SceneObjectMetadata Metadata; + + #region If(Metadata.HasParent) + public ulong ParentObjectId; + #endregion + + #region If(Metadata.HasTransform) + public struct TransformData + { + public Vector3 Position; + public Quaternion Rotation; + } + + public TransformData Transform; + #endregion + + #region If(Metadata.IsReparented) + public bool IsLatestParentSet; + + #region If(IsLatestParentSet) + public ulong? LatestParent; + #endregion + #endregion + + #region If(data.HasNetworkVariables) + public FastBufferWriter NetworkVariableDataWriter; + public FastBufferReader NetworkVariableDataReader; + #endregion + + public unsafe void Serialize(ref FastBufferWriter writer) + { + if (!writer.TryBeginWrite( + sizeof(SceneObjectMetadata) + + (Metadata.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) + + (Metadata.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) + + (Metadata.IsReparented + ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + + (IsLatestParentSet ? FastBufferWriter.GetWriteSize() : 0) + : 0) + + (Metadata.HasNetworkVariables ? sizeof(int) + NetworkVariableDataWriter.Length : 0))) + { + throw new OverflowException("Could not serialize SceneObject: Out of buffer space."); + } + + writer.WriteValue(Metadata); + + if (Metadata.HasParent) + { + writer.WriteValue(ParentObjectId); + } + + if (Metadata.HasTransform) + { + writer.WriteValue(Transform); + } + + if (Metadata.IsReparented) + { + writer.WriteValue(IsLatestParentSet); + if (IsLatestParentSet) + { + writer.WriteValue((ulong)LatestParent); + } + } + + if (Metadata.HasNetworkVariables) + { + writer.WriteValue(NetworkVariableDataWriter.Length); + writer.WriteBytes(NetworkVariableDataWriter.GetUnsafePtr(), NetworkVariableDataWriter.Length); + } + } + + public unsafe void Deserialize(ref FastBufferReader reader) + { + if (!reader.TryBeginRead(sizeof(SceneObjectMetadata))) + { + throw new OverflowException("Could not deserialize SceneObject: Out of buffer space."); + } + reader.ReadValue(out Metadata); + if (!reader.TryBeginRead( + (Metadata.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) + + (Metadata.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) + + (Metadata.IsReparented + ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + + (IsLatestParentSet ? FastBufferWriter.GetWriteSize() : 0) + : 0) + + (Metadata.HasNetworkVariables ? sizeof(int) : 0))) + { + throw new OverflowException("Could not serialize SceneObject: Out of buffer space."); + } + + if (Metadata.HasParent) + { + reader.ReadValue(out ParentObjectId); + } + + if (Metadata.HasTransform) + { + reader.ReadValue(out Transform); + } + + if (Metadata.IsReparented) + { + reader.ReadValue(out IsLatestParentSet); + if (IsLatestParentSet) + { + reader.ReadValue(out ulong latestParent); + LatestParent = latestParent; + } + } + + if (Metadata.HasNetworkVariables) + { + reader.ReadValue(out int length); + if (!reader.TryBeginRead(length)) + { + throw new OverflowException("Could not deserialize SceneObject: Out of buffer space."); + } + + NetworkVariableDataReader = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), + // Allocator.None = view into existing buffer, no allocations or copies and no need to Dispose() + Allocator.None, length); + reader.Seek(reader.Position + length); + } + } + } + + internal SceneObject GetMessageSceneObject(ulong targetClientId) + { + var obj = new SceneObject + { + Metadata = new SceneObject.SceneObjectMetadata + { + IsPlayerObject = IsPlayerObject, + NetworkObjectId = NetworkObjectId, + OwnerClientId = OwnerClientId, + IsSceneObject = IsSceneObject ?? true, + HasNetworkVariables = NetworkManager.NetworkConfig.EnableNetworkVariable, + Hash = HostCheckForGlobalObjectIdHashOverride() + } + }; + + NetworkObject parentNetworkObject = null; + + if (!AlwaysReplicateAsRoot && transform.parent != null) + { + parentNetworkObject = transform.parent.GetComponent(); + } + + if (parentNetworkObject) + { + obj.Metadata.HasParent = true; + obj.ParentObjectId = parentNetworkObject.NetworkObjectId; + } + if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId)) + { + obj.Metadata.HasTransform = true; + obj.Transform = new SceneObject.TransformData + { + Position = transform.position, + Rotation = transform.rotation + }; + } + + var (isReparented, latestParent) = GetNetworkParenting(); + obj.Metadata.IsReparented = isReparented; + if (isReparented) + { + var isLatestParentSet = latestParent != null && latestParent.HasValue; + obj.IsLatestParentSet = isLatestParentSet; + if (isLatestParentSet) + { + obj.LatestParent = latestParent.Value; + } + } + + //If we are including NetworkVariable data + if (NetworkManager.NetworkConfig.EnableNetworkVariable) + { + obj.NetworkVariableDataWriter = new FastBufferWriter(1300, Allocator.TempJob, UInt16.MaxValue); + // Write network variable data + WriteNetworkVariableData(ref obj.NetworkVariableDataWriter, targetClientId); + } + + return obj; + } + /// /// Used to serialize a NetworkObjects during scene synchronization that occurs /// upon a client being approved or a scene transition. @@ -1080,6 +1306,44 @@ internal static NetworkObject DeserializeSceneObject(NetworkBuffer objectStream, return networkObject; } + /// + /// Used to deserialize a serialized scene object which occurs + /// when the client is approved or during a scene transition + /// + /// inbound stream + /// reader for the stream + /// NetworkManager instance + /// optional to use NetworkObject deserialized + internal static NetworkObject AddSceneObject(ref SceneObject sceneObject, NetworkManager networkManager) + { + Vector3? position = null; + Quaternion? rotation = null; + ulong? parentNetworkId = null; + + if (sceneObject.Metadata.HasTransform) + { + position = sceneObject.Transform.Position; + rotation = sceneObject.Transform.Rotation; + } + + if (sceneObject.Metadata.HasParent) + { + parentNetworkId = sceneObject.ParentObjectId; + } + + //Attempt to create a local NetworkObject + var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject( + sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.Hash, + sceneObject.Metadata.OwnerClientId, parentNetworkId, position, rotation, sceneObject.Metadata.IsReparented); + + networkObject?.SetNetworkParenting(sceneObject.Metadata.IsReparented, sceneObject.LatestParent); + + // Spawn the NetworkObject + networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, ref sceneObject, false); + + return networkObject; + } + /// /// Only applies to Host mode. /// Will return the registered source NetworkPrefab's GlobalObjectIdHash if one exists. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs index ad3ea71954..8cb49c8e44 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs @@ -5,8 +5,6 @@ namespace Unity.Netcode internal interface IInternalMessageHandler { NetworkManager NetworkManager { get; } - void HandleConnectionRequest(ulong clientId, Stream stream); - void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime); void HandleAddObject(ulong clientId, Stream stream); void HandleDestroyObject(ulong clientId, Stream stream); void HandleSceneEvent(ulong clientId, Stream stream); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs index e29c199495..6ea6157e23 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs @@ -4,9 +4,17 @@ namespace Unity.Netcode { public interface INetworkHooks { - void OnSendMessage(ulong clientId, Type messageType, NetworkChannel channel, NetworkDelivery delivery); - void OnReceiveMessage(ulong senderId, Type messageType, NetworkChannel channel); - void OnSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); - void OnReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); + void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery); + void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery); + void OnBeforeReceiveMessage(ulong senderId, Type messageType); + void OnAfterReceiveMessage(ulong senderId, Type messageType); + void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); + void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); + void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); + void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); + + + bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery); + bool OnVerifyCanReceive(ulong senderId, Type messageType); } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs index d8318847a5..935e7bd96d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs @@ -14,78 +14,6 @@ public InternalMessageHandler(NetworkManager networkManager) m_NetworkManager = networkManager; } - public void HandleConnectionRequest(ulong clientId, Stream stream) - { - if (NetworkManager.PendingClients.TryGetValue(clientId, out PendingClient client)) - { - // Set to pending approval to prevent future connection requests from being approved - client.ConnectionState = PendingClient.State.PendingApproval; - } - - using var reader = PooledNetworkReader.Get(stream); - ulong configHash = reader.ReadUInt64Packed(); - if (!NetworkManager.NetworkConfig.CompareConfig(configHash)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(NetworkConfig)} mismatch. The configuration between the server and client does not match"); - } - - // Treat this similar to a client that is not approved (remove from pending and disconnect at transport layer) - NetworkManager.PendingClients.Remove(clientId); - NetworkManager.NetworkConfig.NetworkTransport.DisconnectRemoteClient(clientId); - return; - } - - if (NetworkManager.NetworkConfig.ConnectionApproval) - { - byte[] connectionBuffer = reader.ReadByteArray(); - NetworkManager.InvokeConnectionApproval(connectionBuffer, clientId, - (createPlayerObject, playerPrefabHash, approved, position, rotation) => - NetworkManager.HandleApproval(clientId, createPlayerObject, playerPrefabHash, approved, position, rotation)); - } - else - { - NetworkManager.HandleApproval(clientId, NetworkManager.NetworkConfig.PlayerPrefab != null, null, true, null, null); - } - } - - /// - /// Client Side: handles the connection approved message - /// - /// transport derived client identifier (currently not used) - /// incoming stream - /// time this message was received (currently not used) - public void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime) - { - using var reader = PooledNetworkReader.Get(stream); - NetworkManager.LocalClientId = reader.ReadUInt64Packed(); - - int tick = reader.ReadInt32Packed(); - var time = new NetworkTime(NetworkManager.NetworkTickSystem.TickRate, tick); - NetworkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport. - - NetworkManager.ConnectedClients.Add(NetworkManager.LocalClientId, new NetworkClient { ClientId = NetworkManager.LocalClientId }); - - // Only if scene management is disabled do we handle NetworkObject synchronization at this point - if (!NetworkManager.NetworkConfig.EnableSceneManagement) - { - NetworkManager.SpawnManager.DestroySceneObjects(); - - // is not packed! - var objectCount = reader.ReadUInt16(); - for (ushort i = 0; i < objectCount; i++) - { - NetworkObject.DeserializeSceneObject(reader.GetStream() as NetworkBuffer, reader, m_NetworkManager); - } - - // Mark the client being connected - m_NetworkManager.IsConnectedClient = true; - // When scene management is disabled we notify after everything is synchronized - m_NetworkManager.InvokeOnClientConnectedCallback(clientId); - } - } - public void HandleAddObject(ulong clientId, Stream stream) { using var reader = PooledNetworkReader.Get(stream); @@ -257,7 +185,7 @@ public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receive NetworkLog.LogInfo($"Data Header: {nameof(messageType)}={((int)messageType).ToString()}"); } - if (NetworkManager.PendingClients.TryGetValue(clientId, out PendingClient client) && (client.ConnectionState == PendingClient.State.PendingApproval || client.ConnectionState == PendingClient.State.PendingConnection && messageType != MessageQueueContainer.MessageType.ConnectionRequest)) + if (NetworkManager.PendingClients.TryGetValue(clientId, out PendingClient client) && (client.ConnectionState == PendingClient.State.PendingApproval || client.ConnectionState == PendingClient.State.PendingConnection)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs index ccdb4161b4..7f4b1aa268 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs @@ -98,6 +98,7 @@ public void QueueItem( // todo: consider what happens if many clients join and leave the game consecutively // we probably need a cleanup mechanism at some point m_SendDict[clientId] = new SendStream(); + m_SendDict[clientId].Writer.WriteByte(0b11111111); } SendStream sendStream = m_SendDict[clientId]; @@ -116,6 +117,7 @@ public void QueueItem( // clear the batch that was sent from the SendDict sendStream.Buffer.SetLength(0); sendStream.Buffer.Position = 0; + sendStream.Writer.WriteByte(0b11111111); sendStream.Delivery = item.Delivery; } @@ -132,6 +134,7 @@ public void QueueItem( // clear the batch that was sent from the SendDict sendStream.Buffer.SetLength(0); sendStream.Buffer.Position = 0; + sendStream.Writer.WriteByte(0b11111111); sendStream.IsEmpty = true; } } @@ -160,6 +163,7 @@ public void SendItems(int thresholdBytes, SendCallbackType sendCallback) // clear the batch that was sent from the SendDict entry.Value.Buffer.SetLength(0); entry.Value.Buffer.Position = 0; + entry.Value.Writer.WriteByte(0b11111111); entry.Value.IsEmpty = true; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs index 39b9a05985..bb5ae79ec5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageHeader.cs @@ -3,7 +3,6 @@ namespace Unity.Netcode public struct MessageHeader { public byte MessageType; - public NetworkChannel NetworkChannel; public short MessageSize; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs index ae4fd4c25a..4cc58a38e8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs @@ -19,8 +19,6 @@ internal class MessageQueueContainer : INetworkUpdateSystem, IDisposable public enum MessageType { - ConnectionRequest, - ConnectionApproved, ClientRpc, ServerRpc, CreateObject, diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs index 840c8251be..381e980bb7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs @@ -43,20 +43,6 @@ public void ProcessMessage(in MessageFrameItem item) { switch (item.MessageType) { - case MessageQueueContainer.MessageType.ConnectionRequest: - if (m_NetworkManager.IsServer) - { - m_NetworkManager.MessageHandler.HandleConnectionRequest(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.ConnectionApproved: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleConnectionApproved(item.NetworkId, item.NetworkBuffer, item.Timestamp); - } - - break; case MessageQueueContainer.MessageType.ClientRpc: case MessageQueueContainer.MessageType.ServerRpc: // Can rely on currentStage == the original updateStage in the buffer diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages.meta new file mode 100644 index 0000000000..6b3c35953c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fd834639d7f09614fa4f3296921871d8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs new file mode 100644 index 0000000000..b01522e867 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -0,0 +1,85 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Netcode.Messages +{ + internal struct ConnectionApprovedMessage: INetworkMessage + { + public ulong OwnerClientId; + public int NetworkTick; + public int SceneObjectCount; + public NativeArray SceneObjects; + + public void Serialize(ref FastBufferWriter writer) + { + if (!writer.TryBeginWrite(sizeof(ulong) + sizeof(int) + sizeof(int))) + { + throw new OverflowException( + $"Not enough space in the write buffer to serialize {nameof(ConnectionApprovedMessage)}"); + } + writer.WriteValue(OwnerClientId); + writer.WriteValue(NetworkTick); + writer.WriteValue(SceneObjectCount); + if (SceneObjectCount != 0) + { + foreach (var sceneObject in SceneObjects) + { + sceneObject.Serialize(ref writer); + } + + SceneObjects.Dispose(); + } + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int))) + { + return; + } + + var message = new ConnectionApprovedMessage(); + reader.ReadValue(out message.OwnerClientId); + reader.ReadValue(out message.NetworkTick); + reader.ReadValue(out message.SceneObjectCount); + message.SceneObjects = new NativeArray(message.SceneObjectCount, Allocator.Temp); + using (message.SceneObjects) + { + for (var i = 0; i < message.SceneObjectCount; ++i) + { + message.SceneObjects[i] = new NetworkObject.SceneObject(); + message.SceneObjects[i].Deserialize(ref reader); + } + } + message.Handle(context.SenderId, (NetworkManager)context.SystemOwner); + } + + public unsafe void Handle(ulong clientId, NetworkManager networkManager) + { + networkManager.LocalClientId = OwnerClientId; + + var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, NetworkTick); + networkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport. + + networkManager.ConnectedClients.Add(networkManager.LocalClientId, new NetworkClient { ClientId = networkManager.LocalClientId }); + + // Only if scene management is disabled do we handle NetworkObject synchronization at this point + if (!networkManager.NetworkConfig.EnableSceneManagement) + { + networkManager.SpawnManager.DestroySceneObjects(); + + NetworkObject.SceneObject* ptr = (NetworkObject.SceneObject*)SceneObjects.GetUnsafePtr(); + for (ushort i = 0; i < SceneObjectCount; i++) + { + NetworkObject.AddSceneObject(ref ptr[i], networkManager); + } + + // Mark the client being connected + networkManager.IsConnectedClient = true; + // When scene management is disabled we notify after everything is synchronized + networkManager.InvokeOnClientConnectedCallback(clientId); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs.meta index da8bf169f6..31cdaad7f8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8c90016576745774a945081ec90f87f1 +guid: ebbc74ce01b073340aa445f3bd59ff62 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs new file mode 100644 index 0000000000..53f28bece9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs @@ -0,0 +1,145 @@ +using System; +using System.Runtime.InteropServices; +using UnityEditor.VersionControl; +using UnityEngine; + +namespace Unity.Netcode.Messages +{ + internal struct ConnectionRequestMessage: INetworkMessage + { + public ulong ConfigHash; + + [StructLayout(LayoutKind.Explicit, Size = 512)] + public struct ConnectionDataStorage: IFixedArrayStorage + { + + } + + public FixedUnmanagedArray ConnectionData; + + public bool ShouldSendConnectionData; + + public void Serialize(ref FastBufferWriter writer) + { + if (ShouldSendConnectionData) + { + if(!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash) + + FastBufferWriter.GetWriteSize(ConnectionData))) + { + throw new OverflowException( + $"Not enough space in the write buffer to serialize {nameof(ConnectionRequestMessage)}"); + } + writer.WriteValue(ConfigHash); + writer.WriteValue(ConnectionData.Count); + writer.WriteValue(ConnectionData, ConnectionData.Count); + } + else + { + if(!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash))) + { + throw new OverflowException( + $"Not enough space in the write buffer to serialize {nameof(ConnectionRequestMessage)}"); + } + writer.WriteValue(ConfigHash); + } + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + + ConnectionRequestMessage message = new ConnectionRequestMessage(); + if (networkManager.NetworkConfig.ConnectionApproval) + { + if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash) + + FastBufferWriter.GetWriteSize())) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Incomplete connection request message given config - possible {nameof(NetworkConfig)} mismatch."); + } + + networkManager.DisconnectClient(context.SenderId); + return; + } + reader.ReadValue(out message.ConfigHash); + + if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(NetworkConfig)} mismatch. The configuration between the server and client does not match"); + } + + networkManager.DisconnectClient(context.SenderId); + return; + } + + reader.ReadValue(out int length); + if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize() * length)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Incomplete connection request message."); + } + + networkManager.DisconnectClient(context.SenderId); + return; + } + reader.ReadValue(out message.ConnectionData, length); + } + else + { + if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash))) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Incomplete connection request message."); + } + + networkManager.DisconnectClient(context.SenderId); + return; + } + reader.ReadValue(out message.ConfigHash); + + if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(NetworkConfig)} mismatch. The configuration between the server and client does not match"); + } + + networkManager.DisconnectClient(context.SenderId); + return; + } + } + message.Handle(networkManager, context.SenderId); + } + + public void Handle(NetworkManager networkManager, ulong senderId) + { + if (networkManager.PendingClients.TryGetValue(senderId, out PendingClient client)) + { + // Set to pending approval to prevent future connection requests from being approved + client.ConnectionState = PendingClient.State.PendingApproval; + } + + if (networkManager.NetworkConfig.ConnectionApproval) + { + // Note: Delegate creation allocates. + // Note: ToArray() also allocates. :( + networkManager.InvokeConnectionApproval(ConnectionData.ToArray(), senderId, + (createPlayerObject, playerPrefabHash, approved, position, rotation) => + { + var localCreatePlayerObject = createPlayerObject; + networkManager.HandleApproval(senderId, localCreatePlayerObject, playerPrefabHash, approved, + position, rotation); + }); + } + else + { + networkManager.HandleApproval(senderId, networkManager.NetworkConfig.PlayerPrefab != null, null, true, null, null); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs.meta new file mode 100644 index 0000000000..6b6ae756d3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd160468676e06049ad81dcfb22c3dc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index 63f8aa77af..a82954afe4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -145,9 +145,14 @@ public void Dispose() { return; } - foreach (var queue in m_SendQueues) + + var keys = m_SendQueues.GetKeyArray(Allocator.Temp); + using(keys) { - queue.Value.Value.Dispose(); + foreach (var key in keys) + { + ClientDisconnected(key); + } } m_SendQueues.Dispose(); m_IncomingMessageQueue.Dispose(); @@ -197,7 +202,6 @@ private void RegisterMessageType(Type messageType) internal void HandleIncomingData(ulong clientId, ArraySegment data, float receiveTime) { - BatchHeader header; unsafe { fixed (byte* nativeData = data.Array) @@ -209,15 +213,17 @@ internal void HandleIncomingData(ulong clientId, ArraySegment data, float NetworkLog.LogWarning("Received a packet too small to contain a BatchHeader. Ignoring it."); return; } - batchReader.ReadValue(out header); + + batchReader.ReadValue(out BatchHeader batchHeader); for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnReceiveBatch(clientId, header.BatchSize, batchReader.Length); + m_Hooks[hookIdx].OnBeforeReceiveBatch(clientId, batchHeader.BatchSize, batchReader.Length); } - for (var messageIdx = 0; messageIdx < header.BatchSize; ++messageIdx) + for (var messageIdx = 0; messageIdx < batchHeader.BatchSize; ++messageIdx) { + Debug.Log("Receiving a batch"); if (!batchReader.TryBeginRead(sizeof(MessageHeader))) { NetworkLog.LogWarning("Received a batch that didn't have enough data for all of its batches, ending early!"); @@ -241,28 +247,68 @@ internal void HandleIncomingData(ulong clientId, ArraySegment data, float }); batchReader.Seek(batchReader.Position + messageHeader.MessageSize); } + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnAfterReceiveBatch(clientId, batchHeader.BatchSize, batchReader.Length); + } } } } + private bool CanReceive(ulong clientId, Type messageType) + { + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + if (!m_Hooks[hookIdx].OnVerifyCanReceive(clientId, messageType)) + { + return false; + } + } + + return true; + } + public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, float timestamp) { var context = new NetworkContext { SystemOwner = m_Owner, SenderId = senderId, - ReceivingChannel = header.NetworkChannel, Timestamp = timestamp, Header = header }; + var type = m_ReverseTypeMap[header.MessageType]; + if (!CanReceive(senderId, type)) + { + reader.Dispose(); + return; + } + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnReceiveMessage(senderId, m_ReverseTypeMap[header.MessageType], header.NetworkChannel); + m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type); } + Debug.Log($"Processing {type}"); var handler = m_MessageHandlers[header.MessageType]; using (reader) { - handler.Invoke(ref reader, context); + // No user-land message handler exceptions should escape the receive loop. + // If an exception is throw, the message is ignored. + // Example use case: A bad message is received that can't be deserialized and throws + // an OverflowException because it specifies a length greater than the number of bytes in it + // for some dynamic-length value. + try + { + handler.Invoke(ref reader, context); + } + catch (Exception e) + { + Debug.LogError(e); + } + } + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnAfterReceiveMessage(senderId, type); } } @@ -290,13 +336,25 @@ internal void ClientDisconnected(ulong clientId) { queue.Value.GetValueRef(i).Writer.Dispose(); } - queue.Value.Dispose(); - m_SendQueues.Remove(clientId); DynamicUnmanagedArray.ReleaseRef(queue); + m_SendQueues.Remove(clientId); + } + + private bool CanSend(ulong clientId, Type messageType, NetworkDelivery delivery) + { + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + if (!m_Hooks[hookIdx].OnVerifyCanSend(clientId, messageType, delivery)) + { + return false; + } + } + + return true; } - internal unsafe void SendMessage(in T message, NetworkChannel channel, NetworkDelivery delivery, in U clients) + internal unsafe void SendMessage(in T message, NetworkDelivery delivery, in U clientIds) where T: INetworkMessage where U: IReadOnlyList { @@ -306,15 +364,22 @@ internal unsafe void SendMessage(in T message, NetworkChannel channel, Net { message.Serialize(ref tmpSerializer); - for (var i = 0; i < clients.Count; ++i) + for (var i = 0; i < clientIds.Count; ++i) { - var clientId = clients[i]; + var clientId = clientIds[i]; + + if (!CanSend(clientId, typeof(T), delivery)) + { + continue; + } for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnSendMessage(clientId, typeof(T), channel, delivery); + m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(T), delivery); } + Debug.Log($"Sending {typeof(T)} to {clientId}"); + ref var sendQueueItem = ref m_SendQueues[clientId].Value; if (sendQueueItem.Count == 0) { @@ -340,7 +405,6 @@ internal unsafe void SendMessage(in T message, NetworkChannel channel, Net { MessageSize = (short) tmpSerializer.Length, MessageType = m_MessageTypes[typeof(T)], - NetworkChannel = channel }; @@ -359,6 +423,11 @@ internal unsafe void SendMessage(in T message, NetworkChannel channel, Net writeQueueItem.Writer.WriteValue(header); writeQueueItem.Writer.WriteBytes(tmpSerializer.GetUnsafePtr(), tmpSerializer.Length); writeQueueItem.BatchHeader.BatchSize++; + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(T), delivery); + } + Debug.Log($"Sented {typeof(T)} to {clientId} batch size is now {writeQueueItem.BatchHeader.BatchSize}"); } } } @@ -398,11 +467,19 @@ IEnumerator IEnumerable.GetEnumerator() } } - internal unsafe void SendMessage(in T message, NetworkChannel channel, NetworkDelivery delivery, + internal unsafe void SendMessage(in T message, NetworkDelivery delivery, ulong* clientIds, int numClientIds) where T: INetworkMessage { - SendMessage(message, channel, delivery, new PointerListWrapper(clientIds, numClientIds)); + SendMessage(message, delivery, new PointerListWrapper(clientIds, numClientIds)); + } + + internal unsafe void SendMessage(in T message, NetworkDelivery delivery, + ulong clientId) + where T: INetworkMessage + { + ulong* clientIds = stackalloc ulong[] {clientId}; + SendMessage(message, delivery, new PointerListWrapper(clientIds, 1)); } internal void ProcessSendQueues() @@ -419,10 +496,11 @@ internal void ProcessSendQueues() queueItem.Writer.Dispose(); continue; } + Debug.Log($"Sending a batch to {clientId}"); for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); + m_Hooks[hookIdx].OnBeforeSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); } queueItem.Writer.Seek(0); @@ -432,6 +510,11 @@ internal void ProcessSendQueues() m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); queueItem.Writer.Dispose(); + + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnBeforeSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); + } } sendQueueItem.Clear(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs index 0434de5cca..1e43d6be92 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs @@ -4,7 +4,6 @@ public ref struct NetworkContext { public object SystemOwner; public ulong SenderId; - public NetworkChannel ReceivingChannel; public float Timestamp; public MessageHeader Header; } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs deleted file mode 100644 index e3bc26a833..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkManagerMessageSender.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; - -namespace Unity.Netcode -{ - public class NetworkManagerMessageSender : IMessageSender - { - private NetworkManager m_NetworkManager; - - public NetworkManagerMessageSender(NetworkManager manager) - { - m_NetworkManager = manager; - } - - public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) - { - - var length = batchData.Length; - //TODO: Transport needs to have a way to send it data without copying and allocating here. - var bytes = batchData.ToArray(); - var sendBuffer = new ArraySegment(bytes, 0, length); - - //TODO: Transport needs to accept sends by NetworkDelivery instead of NetworkChannel - NetworkChannel channel; - switch (delivery) - { - case NetworkDelivery.Reliable: - channel = NetworkChannel.DefaultMessage; - break; - case NetworkDelivery.Unreliable: - channel = NetworkChannel.SnapshotExchange; - break; - case NetworkDelivery.ReliableSequenced: - channel = NetworkChannel.Internal; - break; - case NetworkDelivery.UnreliableSequenced: - channel = NetworkChannel.UnreliableRpc; - break; - case NetworkDelivery.ReliableFragmentedSequenced: - channel = NetworkChannel.Fragmented; - break; - default: - channel = NetworkChannel.DefaultMessage; - break; - } - m_NetworkManager.NetworkConfig.NetworkTransport.Send(clientId, sendBuffer, channel); - - } - } -} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs index 18b2360436..336d4d730c 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs @@ -222,6 +222,164 @@ public override void ReadField(Stream stream) } } + /// + public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) + { + reader.ReadValue(out ushort deltaCount); + for (int i = 0; i < deltaCount; i++) + { + reader.ReadValue(out NetworkDictionaryEvent.EventType eventType); + switch (eventType) + { + case NetworkDictionaryEvent.EventType.Add: + { + reader.ReadValue(out TKey key); + reader.ReadValue(out TValue value); + m_Dictionary.Add(key, value); + + if (OnDictionaryChanged != null) + { + OnDictionaryChanged(new NetworkDictionaryEvent + { + Type = eventType, + Key = key, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkDictionaryEvent() + { + Type = eventType, + Key = key, + Value = value + }); + } + } + break; + case NetworkDictionaryEvent.EventType.Remove: + { + reader.ReadValue(out TKey key); + TValue value; + m_Dictionary.TryGetValue(key, out value); + m_Dictionary.Remove(key); + + if (OnDictionaryChanged != null) + { + OnDictionaryChanged(new NetworkDictionaryEvent + { + Type = eventType, + Key = key, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkDictionaryEvent() + { + Type = eventType, + Key = key, + Value = value + }); + } + } + break; + case NetworkDictionaryEvent.EventType.RemovePair: + { + reader.ReadValue(out TKey key); + reader.ReadValue(out TValue value); + m_Dictionary.Remove(new KeyValuePair(key, value)); + + if (OnDictionaryChanged != null) + { + OnDictionaryChanged(new NetworkDictionaryEvent + { + Type = eventType, + Key = key, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkDictionaryEvent() + { + Type = eventType, + Key = key, + Value = value + }); + } + } + break; + case NetworkDictionaryEvent.EventType.Clear: + { + //read nothing + m_Dictionary.Clear(); + + if (OnDictionaryChanged != null) + { + OnDictionaryChanged(new NetworkDictionaryEvent + { + Type = eventType + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkDictionaryEvent + { + Type = eventType + }); + } + } + break; + case NetworkDictionaryEvent.EventType.Value: + { + reader.ReadValue(out TKey key); + reader.ReadValue(out TValue value); + + m_Dictionary[key] = value; + + if (OnDictionaryChanged != null) + { + OnDictionaryChanged(new NetworkDictionaryEvent + { + Type = eventType, + Key = key, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkDictionaryEvent() + { + Type = eventType, + Key = key, + Value = value + }); + } + } + break; + } + } + } + + /// + public override void ReadField(ref FastBufferReader reader) + { + m_Dictionary.Clear(); + reader.ReadValue(out ushort entryCount); + for (int i = 0; i < entryCount; i++) + { + reader.ReadValue(out TKey key); + reader.ReadValue(out TValue value); + m_Dictionary.Add(key, value); + } + } + /// public bool TryGetValue(TKey key, out TValue value) { @@ -282,6 +440,58 @@ public override void WriteField(Stream stream) } } + /// + public override void WriteDelta(ref FastBufferWriter writer) + { + writer.WriteValueSafe((ushort)m_DirtyEvents.Count); + for (int i = 0; i < m_DirtyEvents.Count; i++) + { + writer.WriteValueSafe(m_DirtyEvents[i].Type); + switch (m_DirtyEvents[i].Type) + { + case NetworkDictionaryEvent.EventType.Add: + { + writer.WriteValueSafe(m_DirtyEvents[i].Key); + writer.WriteValueSafe(m_DirtyEvents[i].Value); + } + break; + case NetworkDictionaryEvent.EventType.Remove: + { + writer.WriteValueSafe(m_DirtyEvents[i].Key); + } + break; + case NetworkDictionaryEvent.EventType.RemovePair: + { + writer.WriteValueSafe(m_DirtyEvents[i].Key); + writer.WriteValueSafe(m_DirtyEvents[i].Value); + } + break; + case NetworkDictionaryEvent.EventType.Clear: + { + //write nothing + } + break; + case NetworkDictionaryEvent.EventType.Value: + { + writer.WriteValueSafe(m_DirtyEvents[i].Key); + writer.WriteValueSafe(m_DirtyEvents[i].Value); + } + break; + } + } + } + + /// + public override void WriteField(ref FastBufferWriter writer) + { + writer.WriteValueSafe((ushort)m_Dictionary.Count); + foreach (KeyValuePair pair in m_Dictionary) + { + writer.WriteValueSafe(pair.Key); + writer.WriteValueSafe(pair.Value); + } + } + /// public override bool IsDirty() { @@ -456,7 +666,7 @@ public struct NetworkDictionaryEvent /// /// Enum representing the different operations available for triggering an event. /// - public enum EventType + public enum EventType : byte { /// /// Add diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index e95839df17..b369ca4deb 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -125,6 +125,61 @@ public override void WriteField(Stream stream) } } + /// + public override void WriteDelta(ref FastBufferWriter writer) + { + writer.WriteValue((ushort)m_DirtyEvents.Count); + for (int i = 0; i < m_DirtyEvents.Count; i++) + { + writer.WriteValue(m_DirtyEvents[i].Type); + switch (m_DirtyEvents[i].Type) + { + case NetworkListEvent.EventType.Add: + { + writer.WriteValue(m_DirtyEvents[i].Value); + } + break; + case NetworkListEvent.EventType.Insert: + { + writer.WriteValue(m_DirtyEvents[i].Index); + writer.WriteValue(m_DirtyEvents[i].Value); + } + break; + case NetworkListEvent.EventType.Remove: + { + writer.WriteValue(m_DirtyEvents[i].Value); + } + break; + case NetworkListEvent.EventType.RemoveAt: + { + writer.WriteValue(m_DirtyEvents[i].Index); + } + break; + case NetworkListEvent.EventType.Value: + { + writer.WriteValue(m_DirtyEvents[i].Index); + writer.WriteValue(m_DirtyEvents[i].Value); + } + break; + case NetworkListEvent.EventType.Clear: + { + //Nothing has to be written + } + break; + } + } + } + + /// + public override void WriteField(ref FastBufferWriter writer) + { + writer.WriteValue((ushort)m_List.Count); + for (int i = 0; i < m_List.Count; i++) + { + writer.WriteValue(m_List[i]); + } + } + /// public override void ReadField(Stream stream) { @@ -309,6 +364,190 @@ public override void ReadDelta(Stream stream, bool keepDirtyDelta) } + /// + public override void ReadField(ref FastBufferReader reader) + { + m_List.Clear(); + reader.ReadValue(out ushort count); + for (int i = 0; i < count; i++) + { + reader.ReadValue(out T value); + m_List.Add(value); + } + } + + /// + public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) + { + reader.ReadValue(out ushort deltaCount); + for (int i = 0; i < deltaCount; i++) + { + reader.ReadValue(out NetworkListEvent.EventType eventType); + switch (eventType) + { + case NetworkListEvent.EventType.Add: + { + reader.ReadValue(out T value); + m_List.Add(value); + + if (OnListChanged != null) + { + OnListChanged(new NetworkListEvent + { + Type = eventType, + Index = m_List.Count - 1, + Value = m_List[m_List.Count - 1] + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkListEvent() + { + Type = eventType, + Index = m_List.Count - 1, + Value = m_List[m_List.Count - 1] + }); + } + } + break; + case NetworkListEvent.EventType.Insert: + { + reader.ReadValue(out int index); + reader.ReadValue(out T value); + m_List.Insert(index, value); + + if (OnListChanged != null) + { + OnListChanged(new NetworkListEvent + { + Type = eventType, + Index = index, + Value = m_List[index] + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkListEvent() + { + Type = eventType, + Index = index, + Value = m_List[index] + }); + } + } + break; + case NetworkListEvent.EventType.Remove: + { + reader.ReadValue(out T value); + int index = m_List.IndexOf(value); + m_List.RemoveAt(index); + + if (OnListChanged != null) + { + OnListChanged(new NetworkListEvent + { + Type = eventType, + Index = index, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkListEvent() + { + Type = eventType, + Index = index, + Value = value + }); + } + } + break; + case NetworkListEvent.EventType.RemoveAt: + { + reader.ReadValue(out int index); + T value = m_List[index]; + m_List.RemoveAt(index); + + if (OnListChanged != null) + { + OnListChanged(new NetworkListEvent + { + Type = eventType, + Index = index, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkListEvent() + { + Type = eventType, + Index = index, + Value = value + }); + } + } + break; + case NetworkListEvent.EventType.Value: + { + reader.ReadValue(out int index); + reader.ReadValue(out T value); + if (index < m_List.Count) + { + m_List[index] = value; + } + + if (OnListChanged != null) + { + OnListChanged(new NetworkListEvent + { + Type = eventType, + Index = index, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkListEvent() + { + Type = eventType, + Index = index, + Value = value + }); + } + } + break; + case NetworkListEvent.EventType.Clear: + { + //Read nothing + m_List.Clear(); + + if (OnListChanged != null) + { + OnListChanged(new NetworkListEvent + { + Type = eventType, + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkListEvent() + { + Type = eventType + }); + } + } + break; + } + } + } + /// public IEnumerator GetEnumerator() { @@ -461,7 +700,7 @@ public struct NetworkListEvent /// /// Enum representing the different operations available for triggering an event. /// - public enum EventType + public enum EventType: byte { /// /// Add diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs index 10645191f5..8768ab1aea 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs @@ -98,6 +98,46 @@ public override void WriteDelta(Stream stream) } } + /// + public override void WriteField(ref FastBufferWriter writer) + { + writer.WriteValue((ushort)m_Set.Count); + + foreach (T value in m_Set) + { + writer.WriteValue(value); + } + } + + /// + public override void WriteDelta(ref FastBufferWriter writer) + { + writer.WriteValue((ushort)m_DirtyEvents.Count); + for (int i = 0; i < m_DirtyEvents.Count; i++) + { + writer.WriteValue(m_DirtyEvents[i].Type); + + switch (m_DirtyEvents[i].Type) + { + case NetworkSetEvent.EventType.Add: + { + writer.WriteValue(m_DirtyEvents[i].Value); + } + break; + case NetworkSetEvent.EventType.Remove: + { + writer.WriteValue(m_DirtyEvents[i].Value); + } + break; + case NetworkSetEvent.EventType.Clear: + { + //Nothing has to be written + } + break; + } + } + } + /// public override void WriteField(Stream stream) { @@ -207,6 +247,102 @@ public override void ReadDelta(Stream stream, bool keepDirtyDelta) } } + /// + public override void ReadField(ref FastBufferReader reader) + { + m_Set.Clear(); + reader.ReadValue(out ushort count); + + for (int i = 0; i < count; i++) + { + reader.ReadValue(out T obj); + m_Set.Add(obj); + } + } + + /// + public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) + { + reader.ReadValue(out ushort deltaCount); + for (int i = 0; i < deltaCount; i++) + { + reader.ReadValue(out NetworkSetEvent.EventType eventType); + switch (eventType) + { + case NetworkSetEvent.EventType.Add: + { + reader.ReadValue(out T value); + m_Set.Add(value); + + if (OnSetChanged != null) + { + OnSetChanged(new NetworkSetEvent + { + Type = eventType, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkSetEvent() + { + Type = eventType, + Value = value + }); + } + } + break; + case NetworkSetEvent.EventType.Remove: + { + reader.ReadValue(out T value); + m_Set.Remove(value); + + if (OnSetChanged != null) + { + OnSetChanged(new NetworkSetEvent + { + Type = eventType, + Value = value + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkSetEvent() + { + Type = eventType, + Value = value + }); + } + } + break; + case NetworkSetEvent.EventType.Clear: + { + //Read nothing + m_Set.Clear(); + + if (OnSetChanged != null) + { + OnSetChanged(new NetworkSetEvent + { + Type = eventType, + }); + } + + if (keepDirtyDelta) + { + m_DirtyEvents.Add(new NetworkSetEvent() + { + Type = eventType + }); + } + } + break; + } + } + } + /// public IEnumerator GetEnumerator() { @@ -429,7 +565,7 @@ public struct NetworkSetEvent /// /// Enum representing the different operations available for triggering an event. /// - public enum EventType + public enum EventType : byte { /// /// Add diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs new file mode 100644 index 0000000000..ce4298722a --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs @@ -0,0 +1,105 @@ +using System.IO; + +namespace Unity.Netcode +{ + /// + /// Interface for network value containers + /// + public interface INetworkVariable + { + /// + /// Gets or sets the name of the network variable's instance + /// (MemberInfo) where it was declared. + /// + string Name { get; } + + /// + /// Resets the dirty state and marks the variable as synced / clean + /// + void ResetDirty(); + + /// + /// Gets Whether or not the container is dirty + /// + /// Whether or not the container is dirty + bool IsDirty(); + + /// + /// Gets Whether or not a specific client can write to the varaible + /// + /// The clientId of the remote client + /// Whether or not the client can write to the variable + bool CanClientWrite(ulong clientId); + + /// + /// Gets Whether or not a specific client can read to the varaible + /// + /// The clientId of the remote client + /// Whether or not the client can read to the variable + bool CanClientRead(ulong clientId); + + /// + /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer + /// + /// The stream to write the dirty changes to + void WriteDelta(Stream stream); + + /// + /// Writes the complete state of the variable to the writer + /// + /// The stream to write the state to + void WriteField(Stream stream); + + /// + /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer + /// + /// The stream to write the dirty changes to + void WriteDelta(ref FastBufferWriter writer); + + /// + /// Writes the complete state of the variable to the writer + /// + /// The stream to write the state to + void WriteField(ref FastBufferWriter writer); + + /// + /// Reads the complete state from the reader and applies it + /// + /// The stream to read the state from + /// The local network tick at which this var was written, on the machine it was written + /// The remote network tick at which this var was sent by the host + void ReadField(Stream stream); + + /// + /// Reads delta from the reader and applies them to the internal value + /// + /// The stream to read the delta from + /// Whether or not the delta should be kept as dirty or consumed + /// The local network tick at which this var was written, on the machine it was written + /// The remote network tick at which this var was sent by the host + void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta); + + /// + /// Reads the complete state from the reader and applies it + /// + /// The stream to read the state from + /// The local network tick at which this var was written, on the machine it was written + /// The remote network tick at which this var was sent by the host + void ReadField(ref FastBufferReader reader); + + /// + /// Reads delta from the reader and applies them to the internal value + /// + /// The stream to read the delta from + /// Whether or not the delta should be kept as dirty or consumed + /// The local network tick at which this var was written, on the machine it was written + /// The remote network tick at which this var was sent by the host + void ReadDelta(Stream stream, bool keepDirtyDelta); + + /// + /// Sets NetworkBehaviour the container belongs to. + /// + /// The behaviour the container behaves to + void SetNetworkBehaviour(NetworkBehaviour behaviour); + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta new file mode 100644 index 0000000000..8ff8391fb8 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87f1fd4778c2dab4b8bc02c738cade25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs index adde44288c..d8c43e7727 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs @@ -104,6 +104,15 @@ public override void WriteDelta(Stream stream) WriteField(stream); } + /// + /// Writes the variable to the writer + /// + /// The stream to write the value to + public override void WriteDelta(ref FastBufferWriter writer) + { + WriteField(ref writer); + } + /// /// Reads value from the reader and applies it /// @@ -123,17 +132,47 @@ public override void ReadDelta(Stream stream, bool keepDirtyDelta) OnValueChanged?.Invoke(previousValue, m_InternalValue); } + /// + /// Reads value from the reader and applies it + /// + /// The stream to read the value from + /// Whether or not the container should keep the dirty delta, or mark the delta as consumed + public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) + { + T previousValue = m_InternalValue; + reader.ReadValue(out m_InternalValue); + + if (keepDirtyDelta) + { + m_IsDirty = true; + } + + OnValueChanged?.Invoke(previousValue, m_InternalValue); + } + /// public override void ReadField(Stream stream) { ReadDelta(stream, false); } + /// + public override void ReadField(ref FastBufferReader reader) + { + ReadDelta(ref reader, false); + } + /// public override void WriteField(Stream stream) { using var writer = PooledNetworkWriter.Get(stream); writer.WriteObjectPacked(m_InternalValue); //BOX } + + /// + public override void WriteField(ref FastBufferWriter writer) + { + writer.WriteValue(m_InternalValue); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index bc782a7476..db8f263771 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -106,6 +106,18 @@ public virtual bool CanClientWrite(ulong clientId) /// The stream to write the state to public abstract void WriteField(Stream stream); + /// + /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer + /// + /// The stream to write the dirty changes to + public abstract void WriteDelta(ref FastBufferWriter writer); + + /// + /// Writes the complete state of the variable to the writer + /// + /// The stream to write the state to + public abstract void WriteField(ref FastBufferWriter writer); + /// /// Reads the complete state from the reader and applies it /// @@ -118,5 +130,18 @@ public virtual bool CanClientWrite(ulong clientId) /// The stream to read the delta from /// Whether or not the delta should be kept as dirty or consumed public abstract void ReadDelta(Stream stream, bool keepDirtyDelta); + + /// + /// Reads the complete state from the reader and applies it + /// + /// The stream to read the state from + public abstract void ReadField(ref FastBufferReader reader); + + /// + /// Reads delta from the reader and applies them to the internal value + /// + /// The stream to read the delta from + /// Whether or not the delta should be kept as dirty or consumed + public abstract void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs b/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs index 3925af0c87..422a3015b3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs @@ -5,8 +5,6 @@ namespace Unity.Netcode { internal class InternalMessageHandlerProfilingDecorator : IInternalMessageHandler { - private readonly ProfilerMarker m_HandleConnectionRequest = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleConnectionRequest)}"); - private readonly ProfilerMarker m_HandleConnectionApproved = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleConnectionApproved)}"); private readonly ProfilerMarker m_HandleAddObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAddObject)}"); private readonly ProfilerMarker m_HandleDestroyObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleDestroyObject)}"); private readonly ProfilerMarker m_HandleSceneEvent = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleSceneEvent)}"); @@ -29,24 +27,6 @@ internal InternalMessageHandlerProfilingDecorator(IInternalMessageHandler messag public NetworkManager NetworkManager => m_MessageHandler.NetworkManager; - public void HandleConnectionRequest(ulong clientId, Stream stream) - { - m_HandleConnectionRequest.Begin(); - - m_MessageHandler.HandleConnectionRequest(clientId, stream); - - m_HandleConnectionRequest.End(); - } - - public void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime) - { - m_HandleConnectionApproved.Begin(); - - m_MessageHandler.HandleConnectionApproved(clientId, stream, receiveTime); - - m_HandleConnectionApproved.End(); - } - public void HandleAddObject(ulong clientId, Stream stream) { m_HandleAddObject.Begin(); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 88c36a1b61..85263010dc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -674,6 +674,85 @@ public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) } } + /// + /// Read a value of type FixedUnmanagedArray from the buffer. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValue(out FixedUnmanagedArray value, int count) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + int len = sizeof(TPropertyType) * count; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (PositionInternal + len > AllowedReadMark) + { + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + } +#endif + + value = new FixedUnmanagedArray(); + BytewiseUtility.FastCopyBytes((byte*)value.GetArrayPtr(), BufferPointer + PositionInternal, len); + value.Count = len; + PositionInternal += len; + } + + /// + /// Read a value of type FixedUnmanagedArray from the buffer. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValueSafe(out FixedUnmanagedArray value, int count) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + int len = sizeof(TPropertyType) * count; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (PositionInternal + len > AllowedReadMark) + { + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + } +#endif + + value = new FixedUnmanagedArray(); + BytewiseUtility.FastCopyBytes((byte*)value.GetArrayPtr(), BufferPointer + PositionInternal, len); + value.Count = len; + PositionInternal += len; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Obsolete("FixedUnmanagedArray must be written/read using a count.")] + public void ReadValue(out FixedUnmanagedArray value) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Obsolete("FixedUnmanagedArray must be written/read using a count.")] + public void ReadValueSafe(out FixedUnmanagedArray value) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); + } + /// /// Read a value of any unmanaged type to the buffer. /// It will be copied from the buffer exactly as it existed in memory on the writing end. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 4a6a44092c..3137faef80 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -76,7 +76,7 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, size); #endif - BufferPointer = (byte*)buffer; + BufferPointer = (byte*) buffer; PositionInternal = 0; m_Length = 0; CapacityInternal = size; @@ -119,6 +119,7 @@ public void Seek(int where) { m_Length = PositionInternal; } + PositionInternal = where; } @@ -139,6 +140,7 @@ public void Truncate(int where = -1) { PositionInternal = where; } + if (m_Length > where) { m_Length = where; @@ -166,6 +168,7 @@ internal unsafe void Grow(int additionalSizeRequired) { desiredSize *= 2; } + var newSize = Math.Min(desiredSize, MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -173,7 +176,7 @@ internal unsafe void Grow(int additionalSizeRequired) #endif UnsafeUtility.MemCpy(buffer, BufferPointer, Length); UnsafeUtility.Free(BufferPointer, m_Allocator); - BufferPointer = (byte*)buffer; + BufferPointer = (byte*) buffer; CapacityInternal = newSize; } @@ -207,6 +210,7 @@ public bool TryBeginWrite(int bytes) { return false; } + if (CapacityInternal < MaxCapacityInternal) { Grow(bytes); @@ -254,6 +258,7 @@ public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged { return false; } + if (CapacityInternal < MaxCapacityInternal) { Grow(len); @@ -292,6 +297,7 @@ public bool TryBeginWriteInternal(int bytes) { return false; } + if (CapacityInternal < MaxCapacityInternal) { Grow(bytes); @@ -323,6 +329,7 @@ public unsafe byte[] ToArray() { UnsafeUtility.MemCpy(b, BufferPointer, Length); } + return ret; } @@ -358,6 +365,10 @@ public static int GetWriteSize(string s, bool oneByteChars = false) return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); } + public void WriteUnknownSafe(in T value) + { + } + /// /// Writes a string /// @@ -665,7 +676,7 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) /// /// Writer to copy to [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(FastBufferWriter other) + public unsafe void CopyTo(ref FastBufferWriter other) { other.WriteBytes(BufferPointer, PositionInternal); } @@ -677,10 +688,103 @@ public unsafe void CopyTo(FastBufferWriter other) /// /// Writer to copy to [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyFrom(FastBufferWriter other) + public unsafe void CopyFrom(ref FastBufferWriter other) { WriteBytes(other.BufferPointer, other.PositionInternal); } + + + /// + /// Get the size required to write a FixedUnmanagedArray + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(in FixedUnmanagedArray value, int count) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + return count * sizeof(TPropertyType); + } + + /// + /// Write a value of type FixedUnmanagedArray to the buffer. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(in FixedUnmanagedArray value, int count) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + int len = sizeof(TPropertyType) * count; + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (PositionInternal + len > AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + } +#endif + + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)value.GetArrayPtr(), len); + PositionInternal += len; + } + + /// + /// Write a value of type FixedUnmanagedArray to the buffer. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValueSafe(in FixedUnmanagedArray value, int count) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + int len = sizeof(TPropertyType) * count; + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!TryBeginWriteInternal(len)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)value.GetArrayPtr(), len); + PositionInternal += len; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Obsolete("FixedUnmanagedArray must be written/read using a count.")] + public void WriteValue(in FixedUnmanagedArray value) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Obsolete("FixedUnmanagedArray must be written/read using a count.")] + public void WriteValueSafe(in FixedUnmanagedArray value) + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); + } /// /// Get the size required to write an unmanaged value diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 764ebf85a0..ead62a5dd1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Unity.Collections; +using Unity.Netcode.Messages; using UnityEngine; namespace Unity.Netcode @@ -365,6 +367,78 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo networkObject.InvokeBehaviourNetworkSpawn(); } + // Ran on both server and client + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ref NetworkObject.SceneObject sceneObject, bool destroyWithScene) + { + if (networkObject == null) + { + throw new ArgumentNullException(nameof(networkObject), "Cannot spawn null object"); + } + + if (networkObject.IsSpawned) + { + throw new SpawnStateException("Object is already spawned"); + } + + if (sceneObject.Metadata.HasNetworkVariables && NetworkManager.NetworkConfig.EnableNetworkVariable) + { + networkObject.SetNetworkVariableData(ref sceneObject.NetworkVariableDataReader); + } + + if (SpawnedObjects.ContainsKey(sceneObject.Metadata.NetworkObjectId)) + { + Debug.LogWarning($"Trying to spawn {nameof(NetworkObject.NetworkObjectId)} {sceneObject.Metadata.NetworkObjectId} that already exists!"); + return; + } + + networkObject.IsSpawned = true; + + networkObject.IsSceneObject = sceneObject.Metadata.IsSceneObject; + networkObject.NetworkObjectId = sceneObject.Metadata.NetworkObjectId; + + networkObject.DestroyWithScene = sceneObject.Metadata.IsSceneObject || destroyWithScene; + + networkObject.OwnerClientIdInternal = sceneObject.Metadata.OwnerClientId; + networkObject.IsPlayerObject = sceneObject.Metadata.IsPlayerObject; + + SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject); + SpawnedObjectsList.Add(networkObject); + + NetworkManager.NetworkMetrics.TrackNetworkObject(networkObject); + + if (NetworkManager.IsServer) + { + if (sceneObject.Metadata.IsPlayerObject) + { + NetworkManager.ConnectedClients[sceneObject.Metadata.OwnerClientId].PlayerObject = networkObject; + } + else + { + NetworkManager.ConnectedClients[sceneObject.Metadata.OwnerClientId].OwnedObjects.Add(networkObject); + } + } + else if (sceneObject.Metadata.IsPlayerObject && sceneObject.Metadata.OwnerClientId == NetworkManager.LocalClientId) + { + NetworkManager.ConnectedClients[sceneObject.Metadata.OwnerClientId].PlayerObject = networkObject; + } + + if (NetworkManager.IsServer) + { + for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++) + { + if (networkObject.CheckObjectVisibility == null || networkObject.CheckObjectVisibility(NetworkManager.ConnectedClientsList[i].ClientId)) + { + networkObject.Observers.Add(NetworkManager.ConnectedClientsList[i].ClientId); + } + } + } + + networkObject.SetCachedParent(networkObject.transform.parent); + networkObject.ApplyNetworkParenting(); + NetworkObject.CheckOrphanChildren(); + networkObject.InvokeBehaviourNetworkSpawn(); + } + internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject) { if (!NetworkManager.NetworkConfig.UseSnapshotSpawn) @@ -698,6 +772,34 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } } + + + /// + /// This will write all client observable NetworkObjects to the 's stream while also + /// adding the client to each 's list only if + /// observable to the client. + /// Maximum number of objects that could theoretically be serialized is 65536 for now + /// + /// the client identifier used to determine if a spawned NetworkObject is observable + /// contains the writer used for serialization + internal void SerializeObservedNetworkObjects(ulong clientId, ref ConnectionApprovedMessage message) + { + if (SpawnedObjectsList.Count == 0) + { + return; + } + message.SceneObjects = + new NativeArray(SpawnedObjectsList.Count, Allocator.TempJob); + foreach (var sobj in SpawnedObjectsList) + { + if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(clientId)) + { + sobj.Observers.Add(clientId); + message.SceneObjects[message.SceneObjectCount++] = sobj.GetMessageSceneObject(clientId); + } + } + } + /// /// This will write all client observable NetworkObjects to the 's stream while also diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs index 351e8f8109..13b56027cc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; +using UnityEngine; namespace Unity.Netcode { @@ -63,7 +64,7 @@ public unsafe void Add(T item) public unsafe T Pop() { - return m_Data[m_Length--]; + return m_Data[--m_Length]; } public void Clear() diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs new file mode 100644 index 0000000000..b0c138a883 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Netcode +{ + /// + /// For each usage of FixedUnmanagedArray, a storage class needs to be created for it. + /// Rather than providing a huge list of predefined storage classes, each use should just + /// create their own at the correct size (in bytes). The size should be an even multiple + /// of the value size being stored. + /// + /// Example: + /// + /// [StructLayout(LayoutKind.Explicit, Size = 256 * sizeof(int))] + /// struct FixedStorageInt256 : IFixedArrayStorage + /// { + /// } + /// + public interface IFixedArrayStorage + { + + } + + public struct FixedUnmanagedArray : IReadOnlyList + where TPropertyType : unmanaged + where TStorageType : unmanaged, IFixedArrayStorage + { + private int m_Length; + private TStorageType m_Data; + + public int Count + { + get { return m_Length; } + set { m_Length = value; } + } + + public unsafe int Capacity => sizeof(TStorageType)/sizeof(TPropertyType); + + public bool IsReadOnly => false; + + public unsafe TPropertyType[] ToArray() + { + TPropertyType[] ret = new TPropertyType[Count]; + fixed (TPropertyType* b = ret) + { + fixed (TStorageType* ptr = &m_Data) + { + UnsafeUtility.MemCpy(b, ptr, Count * sizeof(TPropertyType)); + } + } + return ret; + } + + public unsafe FixedUnmanagedArray(TPropertyType* seedData, int size) + { + if (size > sizeof(TStorageType)) + { + throw new OverflowException("Seed data was larger than provided storage class."); + } + + m_Data = new TStorageType(); + fixed (TStorageType* ptr = &m_Data) + { + UnsafeUtility.MemCpy(ptr, seedData, size); + } + + m_Length = size; + } + + public unsafe FixedUnmanagedArray(TPropertyType[] seedData) + { + if (seedData.Length > sizeof(TStorageType)) + { + throw new OverflowException("Seed data was larger than provided storage class."); + } + m_Data = new TStorageType(); + fixed (TStorageType* ptr = &m_Data) + fixed (TPropertyType* seedPtr = seedData) + { + UnsafeUtility.MemCpy(ptr, seedPtr, seedData.Length); + } + + m_Length = seedData.Length; + } + + public unsafe FixedUnmanagedArray(TPropertyType[] seedData, int size) + { + if (size > sizeof(TStorageType)) + { + throw new OverflowException("Seed data was larger than provided storage class."); + } + + if (size > seedData.Length) + { + throw new ArgumentException("Size cannot be greater than seed data's length."); + } + + m_Data = new TStorageType(); + fixed (TStorageType* ptr = &m_Data) + fixed (TPropertyType* seedPtr = seedData) + { + UnsafeUtility.MemCpy(ptr, seedPtr, size); + } + + m_Length = size; + } + + public unsafe TPropertyType* GetArrayPtr() + { + fixed (TStorageType* ptr = &m_Data) + { + return (TPropertyType*) ptr; + } + } + + public unsafe TPropertyType this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + fixed (TStorageType* ptr = &m_Data) + { + TPropertyType* reinterpretPtr = (TPropertyType*) ptr; + return reinterpretPtr[index]; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + fixed (TStorageType* ptr = &m_Data) + { + TPropertyType* reinterpretPtr = (TPropertyType*) ptr; + reinterpretPtr[index] = value; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ref TPropertyType GetValueRef(int index) + { + fixed (TStorageType* ptr = &m_Data) + { + TPropertyType* reinterpretPtr = (TPropertyType*) ptr; + return ref reinterpretPtr[index]; + } + } + + public void Add(TPropertyType value) + { + if (m_Length == Capacity) + { + throw new OverflowException("The FixedUnmanagedArray is full."); + } + + this[m_Length++] = value; + } + + public TPropertyType Pop() + { + return this[--m_Length]; + } + + public void Clear() + { + m_Length = 0; + } + + public IEnumerator GetEnumerator() + { + throw new System.NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta new file mode 100644 index 0000000000..5f3b5fd9b4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7bde9c91128507449a535c7df4a029c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs b/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs index 793b2a61a1..7d6e3e19ad 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs @@ -12,10 +12,6 @@ public DummyMessageHandler(NetworkManager networkManager) NetworkManager = networkManager; } - public void HandleConnectionRequest(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleConnectionRequest)); - - public void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime) => VerifyCalled(nameof(HandleConnectionApproved)); - public void HandleAddObject(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleAddObject)); public void HandleDestroyObject(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleDestroyObject)); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs index 63f40d4af6..b5ce9d446c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs @@ -66,7 +66,6 @@ public void WhenHandlingAMessage_ReceiveMethodIsCalled() { MessageSize = (short) UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), - NetworkChannel = NetworkChannel.Internal }; var message = GetMessage(); @@ -98,7 +97,6 @@ public void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingM { MessageSize = (short) UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), - NetworkChannel = NetworkChannel.Internal }; var message = GetMessage(); @@ -133,7 +131,6 @@ public void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled { MessageSize = (short) UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), - NetworkChannel = NetworkChannel.Internal }; var message = GetMessage(); @@ -170,7 +167,6 @@ public void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethod { MessageSize = (short) UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), - NetworkChannel = NetworkChannel.Internal }; var message = GetMessage(); var message2 = GetMessage(); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs index 74fc2771f9..ac7e1e3781 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs @@ -72,7 +72,7 @@ private TestMessage GetMessage() public void WhenSendingMessage_SerializeIsCalled() { var message = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); Assert.IsTrue(TestMessage.Serialized); } @@ -80,7 +80,7 @@ public void WhenSendingMessage_SerializeIsCalled() public void WhenSendingMessage_NothingIsSentBeforeProcessingSendQueue() { var message = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); Assert.IsEmpty(m_MessageSender.MessageQueue); } @@ -88,7 +88,7 @@ public void WhenSendingMessage_NothingIsSentBeforeProcessingSendQueue() public void WhenProcessingSendQueue_MessageIsSent() { var message = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); @@ -98,9 +98,9 @@ public void WhenProcessingSendQueue_MessageIsSent() public void WhenSendingMultipleMessages_MessagesAreBatched() { var message = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); @@ -113,7 +113,7 @@ public void WhenNotExceedingBatchSize_NewBatchesAreNotCreated() var size = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf(); for (var i = 0; i < 1300 / size; ++i) { - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); } m_MessagingSystem.ProcessSendQueues(); @@ -127,7 +127,7 @@ public void WhenExceedingBatchSize_NewBatchesAreCreated() var size = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf(); for (var i = 0; i < (1300 / size) + 1; ++i) { - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); } m_MessagingSystem.ProcessSendQueues(); @@ -141,7 +141,7 @@ public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated() var size = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf(); for (var i = 0; i < (1300 / size) + 1; ++i) { - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.ReliableFragmentedSequenced, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, m_Clients); } m_MessagingSystem.ProcessSendQueues(); @@ -152,9 +152,9 @@ public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated() public void WhenSwitchingDelivery_NewBatchesAreCreated() { var message = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Unreliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Unreliable, m_Clients); m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(2, m_MessageSender.MessageQueue.Count); @@ -164,9 +164,9 @@ public void WhenSwitchingDelivery_NewBatchesAreCreated() public void WhenSwitchingChannel_NewBatchesAreNotCreated() { var message = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message, NetworkChannel.ReliableRpc, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); @@ -177,8 +177,8 @@ public void WhenSendingMessaged_SentDataIsCorrect() { var message = GetMessage(); var message2 = GetMessage(); - m_MessagingSystem.SendMessage(message, NetworkChannel.Internal, NetworkDelivery.Reliable, m_Clients); - m_MessagingSystem.SendMessage(message2, NetworkChannel.ReliableRpc, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); + m_MessagingSystem.SendMessage(message2, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.ProcessSendQueues(); var reader = new FastBufferReader(m_MessageSender.MessageQueue[0], Allocator.Temp); @@ -193,14 +193,12 @@ public void WhenSendingMessaged_SentDataIsCorrect() Assert.AreEqual(2, header.BatchSize); reader.ReadValue(out MessageHeader messageHeader); - Assert.AreEqual(NetworkChannel.Internal, messageHeader.NetworkChannel); Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader.MessageType); Assert.AreEqual(UnsafeUtility.SizeOf(), messageHeader.MessageSize); reader.ReadValue(out TestMessage receivedMessage); Assert.AreEqual(message, receivedMessage); reader.ReadValue(out MessageHeader messageHeader2); - Assert.AreEqual(NetworkChannel.ReliableRpc, messageHeader2.NetworkChannel); Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader2.MessageType); Assert.AreEqual(UnsafeUtility.SizeOf(), messageHeader2.MessageSize); reader.ReadValue(out TestMessage receivedMessage2); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs index c70e3d9ded..f4ca9d0ac9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -35,19 +35,6 @@ public void MessageHandlerReceivedMessageServerClient() // This has to be done post start networkManager.MessageQueueContainer.EnableBatchedMessages(false); - // Should cause log (server only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleConnectionRequest)); - using var messageStream0 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ConnectionRequest, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream0.GetBuffer(), 0, (int)messageStream0.Length), 0); - - // Should not cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream1 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ConnectionApproved, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream1.GetBuffer(), 0, (int)messageStream1.Length), 0); - // Should not cause log (client only) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); @@ -120,19 +107,6 @@ public void MessageHandlerReceivedMessageServerClient() // This has to be done post start (and post restart since the queue container is reset) networkManager.MessageQueueContainer.EnableBatchedMessages(false); - // Should not cause log (server only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream11 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ConnectionRequest, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream11.GetBuffer(), 0, (int)messageStream11.Length), 0); - - // Should cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleConnectionApproved)); - using var messageStream12 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ConnectionApproved, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream12.GetBuffer(), 0, (int)messageStream12.Length), 0); - // Should cause log (client only) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs index 2758d1a154..970e8fe569 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs @@ -14,22 +14,6 @@ public void Setup() m_Decorator = new InternalMessageHandlerProfilingDecorator(new DummyMessageHandler(null)); } - [Test] - public void HandleConnectionRequestCallsUnderlyingHandler() - { - m_Decorator.HandleConnectionRequest(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleConnectionRequest)); - } - - [Test] - public void HandleConnectionApprovedCallsUnderlyingHandler() - { - m_Decorator.HandleConnectionApproved(0, null, 0.0f); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleConnectionApproved)); - } - [Test] public void HandleAddObjectCallsUnderlyingHandler() { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index ae5e1aefa0..88f55a5551 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -135,8 +135,6 @@ protected void RunGameObjectTest(GameObjectTestDelegate testCode) // Set the NetworkConfig networkManager.NetworkConfig = new NetworkConfig() { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, // Set transport NetworkTransport = obj.AddComponent() }; @@ -150,7 +148,7 @@ protected void RunGameObjectTest(GameObjectTestDelegate testCode) finally { UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopHost(); + networkManager.Shutdown(); } } #endregion diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 0f050f6301..876cc4ab12 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -404,8 +404,6 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) // Set the NetworkConfig networkManager.NetworkConfig = new NetworkConfig() { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, // Set transport NetworkTransport = obj.AddComponent() }; @@ -419,7 +417,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) finally { UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopServer(); + networkManager.Shutdown(); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef index 7d4247107b..075e769b1a 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef @@ -4,8 +4,6 @@ "references": [ "Unity.Netcode.Runtime", "Unity.Netcode.Editor", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner", "Unity.Multiplayer.MetricTypes", "Unity.Multiplayer.NetStats" ], diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs index 2c57832285..9bf555c308 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs @@ -44,6 +44,28 @@ public override void WriteField(Stream stream) FieldWritten = true; } + public override void WriteDelta(ref FastBufferWriter writer) + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits((byte)1, 1); + } + writer.WriteValue(k_DummyValue); + + DeltaWritten = true; + } + + public override void WriteField(ref FastBufferWriter writer) + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits((byte)1, 1); + } + writer.WriteValue(k_DummyValue); + + FieldWritten = true; + } + public override void ReadField(Stream stream) { using var reader = PooledNetworkReader.Get(stream); @@ -61,6 +83,32 @@ public override void ReadDelta(Stream stream, bool keepDirtyDelta) DeltaRead = true; } + + public override void ReadField(ref FastBufferReader reader) + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out byte b, 1); + } + + reader.ReadValue(out int i); + Assert.AreEqual(k_DummyValue, i); + + FieldRead = true; + } + + public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out byte b, 1); + } + + reader.ReadValue(out int i); + Assert.AreEqual(k_DummyValue, i); + + DeltaRead = true; + } } public class DummyNetBehaviour : NetworkBehaviour diff --git a/testproject/Assets/Resources.meta b/testproject/Assets/Resources.meta new file mode 100644 index 0000000000..4a95f1abd3 --- /dev/null +++ b/testproject/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c687d205489ca484aa3ee1be21ae9e51 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Resources/PerformanceTestRunInfo.json b/testproject/Assets/Resources/PerformanceTestRunInfo.json new file mode 100644 index 0000000000..755720af66 --- /dev/null +++ b/testproject/Assets/Resources/PerformanceTestRunInfo.json @@ -0,0 +1,79 @@ +{ + "TestSuite": null, + "Date": 1631066863434, + "Player": { + "Development": false, + "ScreenWidth": 0, + "ScreenHeight": 0, + "ScreenRefreshRate": 0, + "Fullscreen": false, + "Vsync": 0, + "AntiAliasing": 0, + "Batchmode": false, + "RenderThreadingMode": "MultiThreaded", + "GpuSkinning": true, + "Platform": null, + "ColorSpace": null, + "AnisotropicFiltering": null, + "BlendWeights": null, + "GraphicsApi": null, + "ScriptingBackend": "IL2CPP", + "AndroidTargetSdkVersion": "AndroidApiLevelAuto", + "AndroidBuildSystem": "Gradle", + "BuildTarget": "WebGL", + "StereoRenderingPath": "MultiPass" + }, + "Hardware": null, + "Editor": { + "Version": "2020.3.12f1", + "Branch": "2020.3/release", + "Changeset": "b3b2c6512326", + "Date": 1623142004 + }, + "Dependencies": [ + "com.unity.collab-proxy@1.5.7", + "com.unity.ide.rider@3.0.5", + "com.unity.ide.visualstudio@2.0.8", + "com.unity.ide.vscode@1.2.3", + "com.unity.netcode.gameobjects@0.2.0", + "com.unity.multiplayer.transport.utp@0.0.1-preview.1", + "com.unity.package-validation-suite@0.19.2-preview", + "com.unity.test-framework@1.1.27", + "com.unity.test-framework.performance@2.3.1-preview", + "com.unity.textmeshpro@3.0.6", + "com.unity.timeline@1.5.2", + "com.unity.ugui@1.0.0", + "com.unity.modules.ai@1.0.0", + "com.unity.modules.androidjni@1.0.0", + "com.unity.modules.animation@1.0.0", + "com.unity.modules.assetbundle@1.0.0", + "com.unity.modules.audio@1.0.0", + "com.unity.modules.cloth@1.0.0", + "com.unity.modules.director@1.0.0", + "com.unity.modules.imageconversion@1.0.0", + "com.unity.modules.imgui@1.0.0", + "com.unity.modules.jsonserialize@1.0.0", + "com.unity.modules.particlesystem@1.0.0", + "com.unity.modules.physics@1.0.0", + "com.unity.modules.physics2d@1.0.0", + "com.unity.modules.screencapture@1.0.0", + "com.unity.modules.terrain@1.0.0", + "com.unity.modules.terrainphysics@1.0.0", + "com.unity.modules.tilemap@1.0.0", + "com.unity.modules.ui@1.0.0", + "com.unity.modules.uielements@1.0.0", + "com.unity.modules.umbra@1.0.0", + "com.unity.modules.unityanalytics@1.0.0", + "com.unity.modules.unitywebrequest@1.0.0", + "com.unity.modules.unitywebrequestassetbundle@1.0.0", + "com.unity.modules.unitywebrequestaudio@1.0.0", + "com.unity.modules.unitywebrequesttexture@1.0.0", + "com.unity.modules.unitywebrequestwww@1.0.0", + "com.unity.modules.vehicles@1.0.0", + "com.unity.modules.video@1.0.0", + "com.unity.modules.vr@1.0.0", + "com.unity.modules.wind@1.0.0", + "com.unity.modules.xr@1.0.0" + ], + "Results": [] +} \ No newline at end of file diff --git a/testproject/Assets/Resources/PerformanceTestRunInfo.json.meta b/testproject/Assets/Resources/PerformanceTestRunInfo.json.meta new file mode 100644 index 0000000000..b3b02decde --- /dev/null +++ b/testproject/Assets/Resources/PerformanceTestRunInfo.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 478110403df6de644a875e5199b9d022 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs index 99af046da1..ef8f955bf6 100644 --- a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs +++ b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs @@ -276,7 +276,7 @@ public IEnumerator ConnectionApprovalMismatchTest([Values(true, false)] bool ena var nextFrameNumber = Time.frameCount + 5; yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); - Assert.True(m_ServerClientDisconnectedInvocations == 3); + Assert.AreEqual(3, m_ServerClientDisconnectedInvocations); } private void Server_OnClientDisconnectedCallback(ulong clientId) From 441d933f474fcfcc32e35475df8e844dbf40e609 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 10 Sep 2021 10:27:03 -0500 Subject: [PATCH 27/58] More converted messages --- .../Runtime/Core/NetworkBehaviour.cs | 197 +---------- .../Runtime/Core/NetworkManager.cs | 135 ++++--- .../Runtime/Core/NetworkObject.cs | 90 ++--- .../Runtime/Core/SnapshotSystem.cs | 334 +++++++----------- .../Runtime/Logging/NetworkLog.cs | 21 +- .../Messaging/IInternalMessageHandler.cs | 6 - .../Runtime/Messaging/INetworkHooks.cs | 6 +- .../Messaging/InternalMessageHandler.cs | 163 --------- .../MessageQueue/MessageQueueContainer.cs | 7 - .../MessageQueue/MessageQueueProcessor.cs | 60 ---- .../Messages/ChangeOwnershipMessage.cs | 53 +++ .../Messages/ChangeOwnershipMessage.cs.meta | 11 + .../Messages/ConnectionApprovedMessage.cs | 49 +-- .../Messages/ConnectionRequestMessage.cs | 4 + .../Messaging/Messages/CreateObjectMessage.cs | 32 ++ .../Messages/CreateObjectMessage.cs.meta | 11 + .../Messages/DestroyObjectMessage.cs | 41 +++ .../Messages/DestroyObjectMessage.cs.meta | 11 + .../Messages/NetworkVariableDeltaMessage.cs | 231 ++++++++++++ .../NetworkVariableDeltaMessage.cs.meta | 11 + .../Messaging/Messages/ParentSyncMessage.cs | 69 ++++ .../Messages/ParentSyncMessage.cs.meta | 11 + .../Messaging/Messages/ServerLogMessage.cs | 53 +++ .../Messages/ServerLogMessage.cs.meta | 11 + .../Messaging/Messages/SnapshotDataMessage.cs | 127 +++++++ .../Messages/SnapshotDataMessage.cs.meta | 11 + .../Messaging/Messages/TimeSyncMessage.cs | 31 ++ .../Messages/TimeSyncMessage.cs.meta | 11 + .../Runtime/Messaging/MessagingSystem.cs | 35 +- .../Runtime/Metrics/MetricHooks.cs | 64 ++++ .../Runtime/Metrics/MetricHooks.cs.meta | 11 + ...nternalMessageHandlerProfilingDecorator.cs | 61 ---- .../Runtime/Profiling/ProfilingHooks.cs | 91 +++++ .../Runtime/Profiling/ProfilingHooks.cs.meta | 11 + .../Runtime/Spawning/NetworkSpawnManager.cs | 111 ++---- .../Tests/Editor/DummyMessageHandler.cs | 14 +- .../NetworkManagerMessageHandlerTests.cs | 80 ----- ...alMessageHandlerProfilingDecoratorTests.cs | 40 --- 38 files changed, 1267 insertions(+), 1048 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 3dc45f01a8..684be7c1e2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -5,6 +5,7 @@ using System.Linq; using System.IO; using Unity.Collections; +using Unity.Netcode.Messages; namespace Unity.Netcode { @@ -464,16 +465,16 @@ internal void InitializeVariables() internal void PreNetworkVariableWrite() { // reset our "which variables got written" data - m_NetworkVariableIndexesToReset.Clear(); - m_NetworkVariableIndexesToResetSet.Clear(); + NetworkVariableIndexesToReset.Clear(); + NetworkVariableIndexesToResetSet.Clear(); } internal void PostNetworkVariableWrite() { // mark any variables we wrote as no longer dirty - for (int i = 0; i < m_NetworkVariableIndexesToReset.Count; i++) + for (int i = 0; i < NetworkVariableIndexesToReset.Count; i++) { - NetworkVariableFields[m_NetworkVariableIndexesToReset[i]].ResetDirty(); + NetworkVariableFields[NetworkVariableIndexesToReset[i]].ResetDirty(); } } @@ -488,8 +489,8 @@ internal void VariableUpdate(ulong clientId) NetworkVariableUpdate(clientId, NetworkBehaviourId); } - private readonly List m_NetworkVariableIndexesToReset = new List(); - private readonly HashSet m_NetworkVariableIndexesToResetSet = new HashSet(); + internal readonly List NetworkVariableIndexesToReset = new List(); + internal readonly HashSet NetworkVariableIndexesToResetSet = new HashSet(); private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) { @@ -510,93 +511,26 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) { for (int j = 0; j < m_DeliveryMappedNetworkVariableIndices.Count; j++) { - using var buffer = PooledNetworkBuffer.Get(); - using var writer = PooledNetworkWriter.Get(buffer); - // TODO: could skip this if no variables dirty, though obsolete w/ Snapshot - writer.WriteUInt64Packed(NetworkObjectId); - writer.WriteUInt16Packed(NetworkObject.GetNetworkBehaviourOrderIndex(this)); - - var bufferSizeCapture = new BufferSizeCapture(buffer); - - var writtenAny = false; + var shouldSend = false; for (int k = 0; k < NetworkVariableFields.Count; k++) { - if (!m_DeliveryMappedNetworkVariableIndices[j].Contains(k)) - { - // This var does not belong to the currently iterating delivery group. - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - writer.WriteUInt16Packed(0); - } - else - { - writer.WriteBool(false); - } - - continue; - } - - // if I'm dirty AND a client, write (server always has all permissions) - // if I'm dirty AND the server AND the client can read me, send. - bool shouldWrite = NetworkVariableFields[k].ShouldWrite(clientId, IsServer); - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - if (!shouldWrite) - { - writer.WriteUInt16Packed(0); - } - } - else - { - writer.WriteBool(shouldWrite); - } - - if (shouldWrite) + if (NetworkVariableFields[k].ShouldWrite(clientId, IsServer)) { - writtenAny = true; - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - using var varBuffer = PooledNetworkBuffer.Get(); - NetworkVariableFields[k].WriteDelta(varBuffer); - varBuffer.PadBuffer(); - - writer.WriteUInt16Packed((ushort)varBuffer.Length); - buffer.CopyFrom(varBuffer); - } - else - { - NetworkVariableFields[k].WriteDelta(buffer); - buffer.PadBuffer(); - } - - if (!m_NetworkVariableIndexesToResetSet.Contains(k)) - { - m_NetworkVariableIndexesToResetSet.Add(k); - m_NetworkVariableIndexesToReset.Add(k); - } - - NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent( - clientId, - NetworkObjectId, - name, - NetworkVariableFields[k].Name, - __getTypeName(), - bufferSizeCapture.Flush()); + shouldSend = true; } } - if (writtenAny) + if (shouldSend) { - var context = NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - MessageQueueContainer.MessageType.NetworkVariableDelta, m_DeliveryTypesForNetworkVariableGroups[j], - new[] { clientId }, NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new NetworkVariableDeltaMessage { - using var nonNullContext = (InternalCommandContext)context; - nonNullContext.NetworkWriter.WriteBytes(buffer.GetBuffer(), buffer.Position); - } + NetworkObjectId = NetworkObjectId, + NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this), + NetworkBehaviour = this, + ClientId = clientId, + DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j] + }; + NetworkManager.SendMessage(message, m_DeliveryTypesForNetworkVariableGroups[j], clientId); } } } @@ -616,99 +550,6 @@ private bool CouldHaveDirtyNetworkVariables() return false; } - internal void HandleNetworkVariableDeltas(Stream stream, ulong clientId) - { - using var reader = PooledNetworkReader.Get(stream); - for (int i = 0; i < NetworkVariableFields.Count; i++) - { - ushort varSize = 0; - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - varSize = reader.ReadUInt16Packed(); - - if (varSize == 0) - { - continue; - } - } - else - { - if (!reader.ReadBool()) - { - continue; - } - } - - if (NetworkManager.IsServer && !NetworkVariableFields[i].CanClientWrite(clientId)) - { - // we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {NetworkObject.GetNetworkBehaviourOrderIndex(this)} - VariableIndex: {i}"); - NetworkLog.LogError($"[{NetworkVariableFields[i].GetType().Name}]"); - } - - stream.Position += varSize; - continue; - } - - //This client wrote somewhere they are not allowed. This is critical - //We can't just skip this field. Because we don't actually know how to dummy read - //That is, we don't know how many bytes to skip. Because the interface doesn't have a - //Read that gives us the value. Only a Read that applies the value straight away - //A dummy read COULD be added to the interface for this situation, but it's just being too nice. - //This is after all a developer fault. A critical error should be fine. - // - TwoTen - if (NetworkLog.CurrentLogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {NetworkObject.GetNetworkBehaviourOrderIndex(this)} - VariableIndex: {i}"); - NetworkLog.LogError($"[{NetworkVariableFields[i].GetType().Name}]"); - } - - return; - } - long readStartPos = stream.Position; - - NetworkVariableFields[i].ReadDelta(stream, NetworkManager.IsServer); - NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived( - clientId, - NetworkObjectId, - name, - NetworkVariableFields[i].Name, - __getTypeName(), - stream.Length); - - (stream as NetworkBuffer).SkipPadBits(); - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - if (stream.Position > (readStartPos + varSize)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning( - $"Var delta read too far. {stream.Position - (readStartPos + varSize)} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {NetworkObject.GetNetworkBehaviourOrderIndex(this)} - VariableIndex: {i}"); - } - - stream.Position = readStartPos + varSize; - } - else if (stream.Position < (readStartPos + varSize)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning( - $"Var delta read too little. {(readStartPos + varSize) - stream.Position} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {NetworkObject.GetNetworkBehaviourOrderIndex(this)} - VariableIndex: {i}"); - } - - stream.Position = readStartPos + varSize; - } - } - } - } - internal void WriteNetworkVariableData(Stream stream, ulong clientId) { if (NetworkVariableFields.Count == 0) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 373299eb79..6f4c8395b4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -81,15 +81,15 @@ public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDeliver { } - public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) + public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes) { } - public void OnBeforeReceiveMessage(ulong senderId, Type messageType) + public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes) { } - public void OnAfterReceiveMessage(ulong senderId, Type messageType) + public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes) { } @@ -458,7 +458,12 @@ private void Initialize(bool server) this.RegisterNetworkUpdate(NetworkUpdateStage.PostLateUpdate); m_MessagingSystem = new MessagingSystem(new NetworkManagerMessageSender(this), this, UInt64.MaxValue); + m_MessagingSystem.Hook(new NetworkManagerHooks(this)); +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_MessagingSystem.Hook(new ProfilingHooks()); +#endif + m_MessagingSystem.Hook(new MetricHooks(this)); LocalClientId = UInt64.MaxValue; @@ -1181,7 +1186,7 @@ private void SendConnectionRequest() new FixedUnmanagedArray(NetworkConfig .ConnectionData) }; - SendMessage(message, NetworkDelivery.Reliable, ServerClientId); + SendMessage(message, NetworkDelivery.ReliableSequenced, ServerClientId); } private IEnumerator ApprovalTimeout(ulong clientId) @@ -1208,8 +1213,6 @@ private IEnumerator ApprovalTimeout(ulong clientId) private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, ArraySegment payload, float receiveTime) { - NetworkMetrics.TrackTransportBytesReceived(payload.Count); - switch (networkEvent) { case NetworkEvent.Connect: @@ -1289,24 +1292,72 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A private readonly NetworkBuffer m_InputBufferWrapper = new NetworkBuffer(new byte[0]); private readonly MessageBatcher m_MessageBatcher = new MessageBatcher(); - public void SendMessage(in T message, NetworkDelivery delivery, in U clientIds) + public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds) where T : INetworkMessage where U : IReadOnlyList { - m_MessagingSystem.SendMessage(message, delivery, clientIds); + // Prevent server sending to itself + if (IsServer) + { + ulong* nonServerIds = stackalloc ulong[clientIds.Count]; + int newIdx = 0; + for (int idx = 0; idx < clientIds.Count; ++idx) + { + if (clientIds[idx] == ServerClientId) + { + continue; + } + + nonServerIds[newIdx++] = clientIds[idx]; + } + + if (newIdx == 0) + { + return 0; + } + return m_MessagingSystem.SendMessage(message, delivery, nonServerIds, newIdx); + } + return m_MessagingSystem.SendMessage(message, delivery, clientIds); } - public unsafe void SendMessage(in T message, NetworkDelivery delivery, + public unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong* clientIds, int numClientIds) where T: INetworkMessage { - m_MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds); + // Prevent server sending to itself + if (IsServer) + { + ulong* nonServerIds = stackalloc ulong[numClientIds]; + int newIdx = 0; + for (int idx = 0; idx < numClientIds; ++idx) + { + if (clientIds[idx] == ServerClientId) + { + continue; + } + + nonServerIds[newIdx++] = clientIds[idx]; + } + + if (newIdx == 0) + { + return 0; + } + return m_MessagingSystem.SendMessage(message, delivery, nonServerIds, newIdx); + } + + return m_MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds); } - public void SendMessage(in T message, NetworkDelivery delivery, ulong clientId) + public int SendMessage(in T message, NetworkDelivery delivery, ulong clientId) where T: INetworkMessage { - m_MessagingSystem.SendMessage(message, delivery, clientId); + // Prevent server sending to itself + if (IsServer && clientId == ServerClientId) + { + return 0; + } + return m_MessagingSystem.SendMessage(message, delivery, clientId); } internal void HandleIncomingData(ulong clientId, ArraySegment payload, float receiveTime) @@ -1525,13 +1576,11 @@ private void SyncTime() NetworkLog.LogInfo("Syncing Time To Clients"); } - ulong[] clientIds = ConnectedClientsIds; - var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.TimeSync, NetworkDelivery.Unreliable, clientIds, NetworkUpdateStage.EarlyUpdate); - if (context != null) + var message = new TimeSyncMessage { - using var nonNullContext = (InternalCommandContext)context; - nonNullContext.NetworkWriter.WriteInt32Packed(NetworkTickSystem.ServerTime.Tick); - } + Tick = NetworkTickSystem.ServerTime.Tick + }; + SendMessage(message, NetworkDelivery.Unreliable, ConnectedClientsIds); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_SyncTime.End(); #endif @@ -1575,7 +1624,13 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? }; if (!NetworkConfig.EnableSceneManagement) { - SpawnManager.SerializeObservedNetworkObjects(ownerClientId, ref message); + if (SpawnManager.SpawnedObjectsList.Count == 0) + { + return; + } + + message.SceneObjectCount = SpawnManager.SpawnedObjectsList.Count; + message.SpawnedObjectsList = SpawnManager.SpawnedObjectsList; InvokeOnClientConnectedCallback(ownerClientId); } // If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization @@ -1623,45 +1678,11 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) continue; //The new client. } - var context = MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.CreateObject, NetworkDelivery.ReliableSequenced, new[] { clientPair.Key }, NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new CreateObjectMessage { - using var nonNullContext = (InternalCommandContext)context; - nonNullContext.NetworkWriter.WriteBool(true); - nonNullContext.NetworkWriter.WriteUInt64Packed(ConnectedClients[clientId].PlayerObject.NetworkObjectId); - nonNullContext.NetworkWriter.WriteUInt64Packed(clientId); - - //Does not have a parent - nonNullContext.NetworkWriter.WriteBool(false); - - // This is not a scene object - nonNullContext.NetworkWriter.WriteBool(false); - - nonNullContext.NetworkWriter.WriteUInt32Packed(playerPrefabHash); - - if (ConnectedClients[clientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[clientId].PlayerObject.IncludeTransformWhenSpawning(clientId)) - { - nonNullContext.NetworkWriter.WriteBool(true); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.x); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.y); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.position.z); - - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.x); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.y); - nonNullContext.NetworkWriter.WriteSinglePacked(ConnectedClients[clientId].PlayerObject.transform.rotation.eulerAngles.z); - } - else - { - nonNullContext.NetworkWriter.WriteBool(false); - } - - nonNullContext.NetworkWriter.WriteBool(false); //No payload data - - if (NetworkConfig.EnableNetworkVariable) - { - ConnectedClients[clientId].PlayerObject.WriteNetworkVariableData(nonNullContext.NetworkWriter.GetStream(), clientPair.Key); - } - } + ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key) + }; + SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 04cf324f07..010895dc3b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using NUnit.Framework; using Unity.Collections; +using Unity.Netcode.Messages; using UnityEngine; using UnityEngine.Profiling.Memory.Experimental; @@ -314,19 +315,13 @@ public void NetworkHide(ulong clientId) } else { - // Send destroy call - var context = NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.DestroyObject, NetworkDelivery.ReliableSequenced, new[] { clientId }, NetworkUpdateStage.PostLateUpdate); - if (context != null) + var message = new DestroyObjectMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - nonNullContext.NetworkWriter.WriteUInt64Packed(NetworkObjectId); - - var size = bufferSizeCapture.StopMeasureSegment(); - NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, NetworkObjectId, name, size); - } + NetworkObjectId = NetworkObjectId + }; + var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, clientId); + // Send destroy call + NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, NetworkObjectId, name, size); } } @@ -720,12 +715,28 @@ private void OnTransformParentChanged() m_IsReparented = true; ApplyNetworkParenting(); - var context = NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ParentSync, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds.Where(id => Observers.Contains(id)).ToArray(), NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new ParentSyncMessage { - using var nonNullContext = (InternalCommandContext)context; - nonNullContext.NetworkWriter.WriteUInt64Packed(NetworkObjectId); - WriteNetworkParenting(nonNullContext.NetworkWriter, m_IsReparented, m_LatestParent); + NetworkObjectId = NetworkObjectId, + IsReparented = m_IsReparented, + IsLatestParentSet = m_LatestParent != null && m_LatestParent.HasValue, + LatestParent = m_LatestParent + }; + + unsafe + { + var maxCount = NetworkManager.ConnectedClientsIds.Length; + ulong* clientIds = stackalloc ulong[maxCount]; + int idx = 0; + foreach (var clientId in NetworkManager.ConnectedClientsIds) + { + if (Observers.Contains(clientId)) + { + clientIds[idx++] = clientId; + } + } + + NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, clientIds, idx); } } @@ -968,8 +979,8 @@ public struct TransformData #endregion #region If(data.HasNetworkVariables) - public FastBufferWriter NetworkVariableDataWriter; - public FastBufferReader NetworkVariableDataReader; + public NetworkObject OwnerObject; + public ulong TargetClientId; #endregion public unsafe void Serialize(ref FastBufferWriter writer) @@ -981,8 +992,7 @@ public unsafe void Serialize(ref FastBufferWriter writer) (Metadata.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + (IsLatestParentSet ? FastBufferWriter.GetWriteSize() : 0) - : 0) + - (Metadata.HasNetworkVariables ? sizeof(int) + NetworkVariableDataWriter.Length : 0))) + : 0))) { throw new OverflowException("Could not serialize SceneObject: Out of buffer space."); } @@ -1010,8 +1020,7 @@ public unsafe void Serialize(ref FastBufferWriter writer) if (Metadata.HasNetworkVariables) { - writer.WriteValue(NetworkVariableDataWriter.Length); - writer.WriteBytes(NetworkVariableDataWriter.GetUnsafePtr(), NetworkVariableDataWriter.Length); + OwnerObject.WriteNetworkVariableData(ref writer, TargetClientId); } } @@ -1028,10 +1037,9 @@ public unsafe void Deserialize(ref FastBufferReader reader) (Metadata.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + (IsLatestParentSet ? FastBufferWriter.GetWriteSize() : 0) - : 0) + - (Metadata.HasNetworkVariables ? sizeof(int) : 0))) + : 0))) { - throw new OverflowException("Could not serialize SceneObject: Out of buffer space."); + throw new OverflowException("Could not deserialize SceneObject: Out of buffer space."); } if (Metadata.HasParent) @@ -1053,20 +1061,6 @@ public unsafe void Deserialize(ref FastBufferReader reader) LatestParent = latestParent; } } - - if (Metadata.HasNetworkVariables) - { - reader.ReadValue(out int length); - if (!reader.TryBeginRead(length)) - { - throw new OverflowException("Could not deserialize SceneObject: Out of buffer space."); - } - - NetworkVariableDataReader = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), - // Allocator.None = view into existing buffer, no allocations or copies and no need to Dispose() - Allocator.None, length); - reader.Seek(reader.Position + length); - } } } @@ -1082,7 +1076,9 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) IsSceneObject = IsSceneObject ?? true, HasNetworkVariables = NetworkManager.NetworkConfig.EnableNetworkVariable, Hash = HostCheckForGlobalObjectIdHashOverride() - } + }, + OwnerObject = this, + TargetClientId = targetClientId }; NetworkObject parentNetworkObject = null; @@ -1118,14 +1114,6 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) obj.LatestParent = latestParent.Value; } } - - //If we are including NetworkVariable data - if (NetworkManager.NetworkConfig.EnableNetworkVariable) - { - obj.NetworkVariableDataWriter = new FastBufferWriter(1300, Allocator.TempJob, UInt16.MaxValue); - // Write network variable data - WriteNetworkVariableData(ref obj.NetworkVariableDataWriter, targetClientId); - } return obj; } @@ -1314,7 +1302,7 @@ internal static NetworkObject DeserializeSceneObject(NetworkBuffer objectStream, /// reader for the stream /// NetworkManager instance /// optional to use NetworkObject deserialized - internal static NetworkObject AddSceneObject(ref SceneObject sceneObject, NetworkManager networkManager) + internal static NetworkObject AddSceneObject(in SceneObject sceneObject, ref FastBufferReader variableData, NetworkManager networkManager) { Vector3? position = null; Quaternion? rotation = null; @@ -1339,7 +1327,7 @@ internal static NetworkObject AddSceneObject(ref SceneObject sceneObject, Networ networkObject?.SetNetworkParenting(sceneObject.Metadata.IsReparented, sceneObject.LatestParent); // Spawn the NetworkObject - networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, ref sceneObject, false); + networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, ref variableData, false); return networkObject; } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index e4affa5472..7270028abe 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -1,6 +1,10 @@ using System; using System.Collections.Generic; using System.IO; +using PlasticGui.Configuration; +using Unity.Collections; +using Unity.Netcode.Messages; +using UnityEditor.VersionControl; using UnityEngine; namespace Unity.Netcode @@ -232,25 +236,7 @@ internal void AddDespawn(SnapshotDespawnCommand command) } } - /// - /// Write an Entry to send - /// Must match ReadEntry - /// - /// The writer to write the entry to - internal void WriteEntry(NetworkWriter writer, in Entry entry) - { - //todo: major refactor. - // use blittable types and copy variable in memory locally - // only serialize when put on the wire for network transfer - writer.WriteUInt64Packed(entry.Key.NetworkObjectId); - writer.WriteUInt16(entry.Key.BehaviourIndex); - writer.WriteUInt16(entry.Key.VariableIndex); - writer.WriteInt32Packed(entry.Key.TickWritten); - writer.WriteUInt16(entry.Position); - writer.WriteUInt16(entry.Length); - } - - internal ClientData.SentSpawn WriteSpawn(in ClientData clientData, NetworkWriter writer, in SnapshotSpawnCommand spawn) + internal ClientData.SentSpawn GetSpawnData(in ClientData clientData, in SnapshotSpawnCommand spawn, out SnapshotDataMessage.SpawnData data) { // remember which spawn we sent this connection with which sequence number // that way, upon ack, we can track what is being ack'ed @@ -259,23 +245,25 @@ internal ClientData.SentSpawn WriteSpawn(in ClientData clientData, NetworkWriter sentSpawn.Tick = spawn.TickWritten; sentSpawn.SequenceNumber = clientData.SequenceNumber; - writer.WriteUInt64Packed(spawn.NetworkObjectId); - writer.WriteUInt64Packed(spawn.GlobalObjectIdHash); - writer.WriteBool(spawn.IsSceneObject); - - writer.WriteBool(spawn.IsPlayerObject); - writer.WriteUInt64Packed(spawn.OwnerClientId); - writer.WriteUInt64Packed(spawn.ParentNetworkId); - writer.WriteVector3(spawn.ObjectPosition); - writer.WriteRotation(spawn.ObjectRotation); - writer.WriteVector3(spawn.ObjectScale); - - writer.WriteInt32Packed(spawn.TickWritten); - + data = new SnapshotDataMessage.SpawnData + { + NetworkObjectId = spawn.NetworkObjectId, + Hash = spawn.GlobalObjectIdHash, + IsSceneObject = spawn.IsSceneObject, + + IsPlayerObject = spawn.IsPlayerObject, + OwnerClientId = spawn.OwnerClientId, + ParentNetworkId = spawn.ParentNetworkId, + Position = spawn.ObjectPosition, + Rotation = spawn.ObjectRotation, + Scale = spawn.ObjectScale, + + TickWritten = spawn.TickWritten + }; return sentSpawn; } - internal ClientData.SentSpawn WriteDespawn(in ClientData clientData, NetworkWriter writer, in SnapshotDespawnCommand despawn) + internal ClientData.SentSpawn GetDespawnData(in ClientData clientData, in SnapshotDespawnCommand despawn, out SnapshotDataMessage.DespawnData data) { // remember which spawn we sent this connection with which sequence number // that way, upon ack, we can track what is being ack'ed @@ -284,8 +272,11 @@ internal ClientData.SentSpawn WriteDespawn(in ClientData clientData, NetworkWrit sentSpawn.Tick = despawn.TickWritten; sentSpawn.SequenceNumber = clientData.SequenceNumber; - writer.WriteUInt64Packed(despawn.NetworkObjectId); - writer.WriteInt32Packed(despawn.TickWritten); + data = new SnapshotDataMessage.DespawnData + { + NetworkObjectId = despawn.NetworkObjectId, + TickWritten = despawn.TickWritten + }; return sentSpawn; } @@ -294,44 +285,44 @@ internal ClientData.SentSpawn WriteDespawn(in ClientData clientData, NetworkWrit /// Must match WriteEntry /// /// The readed to read the entry from - internal Entry ReadEntry(NetworkReader reader) + internal Entry ReadEntry(SnapshotDataMessage.EntryData data) { Entry entry; - entry.Key.NetworkObjectId = reader.ReadUInt64Packed(); - entry.Key.BehaviourIndex = reader.ReadUInt16(); - entry.Key.VariableIndex = reader.ReadUInt16(); - entry.Key.TickWritten = reader.ReadInt32Packed(); - entry.Position = reader.ReadUInt16(); - entry.Length = reader.ReadUInt16(); + entry.Key.NetworkObjectId = data.NetworkObjectId; + entry.Key.BehaviourIndex = data.BehaviourIndex; + entry.Key.VariableIndex = data.VariableIndex; + entry.Key.TickWritten = data.TickWritten; + entry.Position = data.Position; + entry.Length = data.Length; return entry; } - internal SnapshotSpawnCommand ReadSpawn(NetworkReader reader) + internal SnapshotSpawnCommand ReadSpawn(SnapshotDataMessage.SpawnData data) { var command = new SnapshotSpawnCommand(); - command.NetworkObjectId = reader.ReadUInt64Packed(); - command.GlobalObjectIdHash = (uint)reader.ReadUInt64Packed(); - command.IsSceneObject = reader.ReadBool(); - command.IsPlayerObject = reader.ReadBool(); - command.OwnerClientId = reader.ReadUInt64Packed(); - command.ParentNetworkId = reader.ReadUInt64Packed(); - command.ObjectPosition = reader.ReadVector3(); - command.ObjectRotation = reader.ReadRotation(); - command.ObjectScale = reader.ReadVector3(); + command.NetworkObjectId = data.NetworkObjectId; + command.GlobalObjectIdHash = data.Hash; + command.IsSceneObject = data.IsSceneObject; + command.IsPlayerObject = data.IsPlayerObject; + command.OwnerClientId = data.OwnerClientId; + command.ParentNetworkId = data.ParentNetworkId; + command.ObjectPosition = data.Position; + command.ObjectRotation = data.Rotation; + command.ObjectScale = data.Scale; - command.TickWritten = reader.ReadInt32Packed(); + command.TickWritten = data.TickWritten; return command; } - internal SnapshotDespawnCommand ReadDespawn(NetworkReader reader) + internal SnapshotDespawnCommand ReadDespawn(SnapshotDataMessage.DespawnData data) { var command = new SnapshotDespawnCommand(); - command.NetworkObjectId = reader.ReadUInt64Packed(); - command.TickWritten = reader.ReadInt32Packed(); + command.NetworkObjectId = data.NetworkObjectId; + command.TickWritten = data.TickWritten; return command; } @@ -369,10 +360,9 @@ internal void AllocateEntry(ref Entry entry, int index, int size) /// /// The NetworkReader to read our buffer of variables from /// The stream to read our buffer of variables from - internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) + internal void ReadBuffer(in SnapshotDataMessage message) { - int snapshotSize = reader.ReadUInt16(); - snapshotStream.Read(RecvBuffer, 0, snapshotSize); + RecvBuffer = message.ReceiveMainBuffer.ToArray(); } /// @@ -380,16 +370,15 @@ internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) /// Stores the entry. Allocates memory if needed. The actual buffer will be read later /// /// The reader to read the index from - internal void ReadIndex(NetworkReader reader) + internal void ReadIndex(in SnapshotDataMessage message) { Entry entry; - short entries = reader.ReadInt16(); - for (var i = 0; i < entries; i++) + for (var i = 0; i < message.Entries.Length; i++) { bool added = false; - entry = ReadEntry(reader); + entry = ReadEntry(message.Entries[i]); int pos = Find(entry.Key);// should return if there's anything more recent if (pos == Entry.NotFound) @@ -425,17 +414,14 @@ internal void ReadIndex(NetworkReader reader) } } - internal void ReadSpawns(NetworkReader reader) + internal void ReadSpawns(in SnapshotDataMessage message) { SnapshotSpawnCommand spawnCommand; SnapshotDespawnCommand despawnCommand; - short spawnCount = reader.ReadInt16(); - short despawnCount = reader.ReadInt16(); - - for (var i = 0; i < spawnCount; i++) + for (var i = 0; i < message.Spawns.Length; i++) { - spawnCommand = ReadSpawn(reader); + spawnCommand = ReadSpawn(message.Spawns[i]); if (TickAppliedSpawn.ContainsKey(spawnCommand.NetworkObjectId) && spawnCommand.TickWritten <= TickAppliedSpawn[spawnCommand.NetworkObjectId]) @@ -458,9 +444,9 @@ internal void ReadSpawns(NetworkReader reader) NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, null, false, false); } } - for (var i = 0; i < despawnCount; i++) + for (var i = 0; i < message.Despawns.Length; i++) { - despawnCommand = ReadDespawn(reader); + despawnCommand = ReadDespawn(message.Despawns[i]); if (TickAppliedDespawn.ContainsKey(despawnCommand.NetworkObjectId) && despawnCommand.TickWritten <= TickAppliedDespawn[despawnCommand.NetworkObjectId]) @@ -479,10 +465,10 @@ internal void ReadSpawns(NetworkReader reader) } } - internal void ReadAcks(ulong clientId, ClientData clientData, NetworkReader reader, ConnectionRtt connection) + internal void ReadAcks(ulong clientId, ClientData clientData, in SnapshotDataMessage message, ConnectionRtt connection) { - ushort ackSequence = reader.ReadUInt16(); - ushort seqMask = reader.ReadUInt16(); + ushort ackSequence = message.Ack.LastReceivedSequence; + ushort seqMask = message.Ack.ReceivedSequenceMask; // process the latest acknowledgment ProcessSingleAck(ackSequence, clientId, clientData, connection); @@ -728,33 +714,37 @@ private void SendSnapshot(ulong clientId) m_ConnectionRtts[clientId].NotifySend(m_ClientData[clientId].SequenceNumber, Time.unscaledTime); var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.SnapshotData, NetworkDelivery.Unreliable, new[] { clientId }, NetworkUpdateLoop.UpdateStage); - if (context != null) + + var sequence = m_ClientData[clientId].SequenceNumber; + var message = new SnapshotDataMessage { - using var nonNullContext = (InternalCommandContext)context; - var sequence = m_ClientData[clientId].SequenceNumber; - - // write the tick and sequence header - nonNullContext.NetworkWriter.WriteInt32Packed(m_CurrentTick); - nonNullContext.NetworkWriter.WriteUInt16(sequence); - - var buffer = (NetworkBuffer)nonNullContext.NetworkWriter.GetStream(); - - using var writer = PooledNetworkWriter.Get(buffer); - // write the snapshot: buffer, index, spawns, despawns - writer.WriteUInt16(SentinelBefore); - WriteBuffer(buffer); - WriteIndex(buffer); - WriteAcks(buffer, clientId); - WriteSpawns(buffer, clientId); - writer.WriteUInt16(SentinelAfter); - - m_ClientData[clientId].LastReceivedSequence = 0; - - // todo: this is incorrect (well, sub-optimal) - // we should still continue ack'ing past messages, in case this one is dropped - m_ClientData[clientId].ReceivedSequenceMask = 0; - m_ClientData[clientId].SequenceNumber++; - } + CurrentTick = m_CurrentTick, + Sequence = sequence, + Range = (ushort)m_Snapshot.Allocator.Range, + + // todo --M1-- + // this sends the whole buffer + // we'll need to build a per-client list + SendMainBuffer = m_Snapshot.MainBuffer, + + Ack = new SnapshotDataMessage.AckData + { + LastReceivedSequence = m_ClientData[clientId].LastReceivedSequence, + ReceivedSequenceMask = m_ClientData[clientId].ReceivedSequenceMask + } + }; + + + // write the snapshot: buffer, index, spawns, despawns + WriteIndex(ref message); + WriteSpawns(ref message, clientId); + + m_ClientData[clientId].LastReceivedSequence = 0; + + // todo: this is incorrect (well, sub-optimal) + // we should still continue ack'ing past messages, in case this one is dropped + m_ClientData[clientId].ReceivedSequenceMask = 0; + m_ClientData[clientId].SequenceNumber++; } // Checks if a given SpawnCommand should be written to a Snapshot Message @@ -789,7 +779,7 @@ private bool ShouldWriteDespawn(in SnapshotDespawnCommand despawnCommand) return (1 << diff) > (despawnCommand.TimesWritten - 1); } - private void WriteSpawns(NetworkBuffer buffer, ulong clientId) + private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) { var spawnWritten = 0; var despawnWritten = 0; @@ -816,35 +806,31 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId) clientData.NextDespawnIndex = 0; } - using var writer = PooledNetworkWriter.Get(buffer); - var positionSpawns = writer.GetStream().Position; - writer.WriteInt16((short)m_Snapshot.NumSpawns); - var positionDespawns = writer.GetStream().Position; - writer.WriteInt16((short)m_Snapshot.NumDespawns); + message.Spawns = new NativeArray(m_Snapshot.NumSpawns, Allocator.TempJob); + message.Despawns = new NativeArray(m_Snapshot.NumDespawns, Allocator.TempJob); + var spawnUsage = 0; for (var j = 0; j < m_Snapshot.NumSpawns && !overSize; j++) { var index = clientData.NextSpawnIndex; - var savedPosition = writer.GetStream().Position; // todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns if (m_Snapshot.Spawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteSpawn(m_Snapshot.Spawns[index])*/) { - var sentSpawn = m_Snapshot.WriteSpawn(clientData, writer, in m_Snapshot.Spawns[index]); + spawnUsage += FastBufferWriter.GetWriteSize(); // limit spawn sizes, compare current pos to very first position we wrote to - if (writer.GetStream().Position - positionSpawns > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage) + if (spawnUsage > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage) { overSize = true; - // revert back the position to undo the write - writer.GetStream().Position = savedPosition; - } - else - { - m_Snapshot.Spawns[index].TimesWritten++; - clientData.SentSpawns.Add(sentSpawn); - spawnWritten++; + break; } + var sentSpawn = m_Snapshot.GetSpawnData(clientData, in m_Snapshot.Spawns[index], out var spawn); + message.Spawns[j] = spawn; + + m_Snapshot.Spawns[index].TimesWritten++; + clientData.SentSpawns.Add(sentSpawn); + spawnWritten++; } clientData.NextSpawnIndex = (clientData.NextSpawnIndex + 1) % m_Snapshot.NumSpawns; } @@ -860,81 +846,51 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId) for (var j = 0; j < m_Snapshot.NumDespawns && !overSize; j++) { var index = clientData.NextDespawnIndex; - var savedPosition = writer.GetStream().Position; // todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns if (m_Snapshot.Despawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteDespawn(m_Snapshot.Despawns[index])*/) { - var sentDespawn = m_Snapshot.WriteDespawn(clientData, writer, in m_Snapshot.Despawns[index]); + spawnUsage += FastBufferWriter.GetWriteSize(); // limit spawn sizes, compare current pos to very first position we wrote to - if (writer.GetStream().Position - positionSpawns > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage) + if (spawnUsage > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage) { overSize = true; - // revert back the position to undo the write - writer.GetStream().Position = savedPosition; - } - else - { - m_Snapshot.Despawns[index].TimesWritten++; - clientData.SentSpawns.Add(sentDespawn); - despawnWritten++; - } + break; + } + var sentDespawn = m_Snapshot.GetDespawnData(clientData, in m_Snapshot.Despawns[index], out var despawn); + message.Despawns[j] = despawn; + m_Snapshot.Despawns[index].TimesWritten++; + clientData.SentSpawns.Add(sentDespawn); + despawnWritten++; } clientData.NextDespawnIndex = (clientData.NextDespawnIndex + 1) % m_Snapshot.NumDespawns; } - - long positionAfter = 0; - - positionAfter = writer.GetStream().Position; - writer.GetStream().Position = positionSpawns; - writer.WriteInt16((short)spawnWritten); - writer.GetStream().Position = positionAfter; - - positionAfter = writer.GetStream().Position; - writer.GetStream().Position = positionDespawns; - writer.WriteInt16((short)despawnWritten); - writer.GetStream().Position = positionAfter; - } - - private void WriteAcks(NetworkBuffer buffer, ulong clientId) - { - using var writer = PooledNetworkWriter.Get(buffer); - // todo: revisit whether 16-bit is enough for LastReceivedSequence - writer.WriteUInt16(m_ClientData[clientId].LastReceivedSequence); - writer.WriteUInt16(m_ClientData[clientId].ReceivedSequenceMask); } /// /// Write the snapshot index to a buffer /// /// The buffer to write the index to - private void WriteIndex(NetworkBuffer buffer) + private void WriteIndex(ref SnapshotDataMessage message) { - using var writer = PooledNetworkWriter.Get(buffer); - writer.WriteInt16((short)m_Snapshot.LastEntry); + message.Entries = new NativeArray(m_Snapshot.LastEntry, Allocator.TempJob); for (var i = 0; i < m_Snapshot.LastEntry; i++) { - m_Snapshot.WriteEntry(writer, in m_Snapshot.Entries[i]); + var entryMeta = m_Snapshot.Entries[i]; + var entry = entryMeta.Key; + message.Entries[i] = new SnapshotDataMessage.EntryData + { + NetworkObjectId = entry.NetworkObjectId, + BehaviourIndex = entry.BehaviourIndex, + VariableIndex = entry.VariableIndex, + TickWritten = entry.TickWritten, + Position = entryMeta.Position, + Length = entryMeta.Length + }; } } - /// - /// Write the buffer of a snapshot - /// Must match ReadBuffer - /// - /// The NetworkBuffer to write our buffer of variables to - private void WriteBuffer(NetworkBuffer buffer) - { - using var writer = PooledNetworkWriter.Get(buffer); - writer.WriteUInt16((ushort)m_Snapshot.Allocator.Range); - - // todo --M1-- - // this sends the whole buffer - // we'll need to build a per-client list - buffer.Write(m_Snapshot.MainBuffer, 0, m_Snapshot.Allocator.Range); - } - internal void Spawn(SnapshotSpawnCommand command) { command.TickWritten = m_CurrentTick; @@ -998,31 +954,21 @@ private void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBase netw /// /// /// The stream to read from - internal void ReadSnapshot(ulong clientId, Stream snapshotStream) + internal void HandleSnapshot(ulong clientId, in SnapshotDataMessage message) { - // todo: temporary hack around bug - if (!m_NetworkManager.IsServer) - { - clientId = m_NetworkManager.ServerClientId; - } - - using var reader = PooledNetworkReader.Get(snapshotStream); // make sure we have a ClientData entry for each client if (!m_ClientData.ContainsKey(clientId)) { m_ClientData.Add(clientId, new ClientData()); } - var snapshotTick = reader.ReadInt32Packed(); - var sequence = reader.ReadUInt16(); - - if (sequence >= m_ClientData[clientId].LastReceivedSequence) + if (message.Sequence >= m_ClientData[clientId].LastReceivedSequence) { if (m_ClientData[clientId].ReceivedSequenceMask != 0) { // since each bit in ReceivedSequenceMask is relative to the last received sequence // we need to shift all the bits by the difference in sequence - var shift = sequence - m_ClientData[clientId].LastReceivedSequence; + var shift = message.Sequence - m_ClientData[clientId].LastReceivedSequence; if (shift < sizeof(ushort) * 8) { m_ClientData[clientId].ReceivedSequenceMask <<= shift; @@ -1037,14 +983,14 @@ internal void ReadSnapshot(ulong clientId, Stream snapshotStream) { // because the bit we're adding for the previous ReceivedSequenceMask // was implicit, it needs to be shift by one less - var shift = sequence - 1 - m_ClientData[clientId].LastReceivedSequence; + var shift = message.Sequence - 1 - m_ClientData[clientId].LastReceivedSequence; if (shift < sizeof(ushort) * 8) { m_ClientData[clientId].ReceivedSequenceMask |= (ushort)(1 << shift); } } - m_ClientData[clientId].LastReceivedSequence = sequence; + m_ClientData[clientId].LastReceivedSequence = message.Sequence; } else { @@ -1055,22 +1001,10 @@ internal void ReadSnapshot(ulong clientId, Stream snapshotStream) // without this, we incur extra retransmit, not a catastrophic failure } - var sentinel = reader.ReadUInt16(); - if (sentinel != SentinelBefore) - { - Debug.Log("Critical : snapshot integrity (before)"); - } - - m_Snapshot.ReadBuffer(reader, snapshotStream); - m_Snapshot.ReadIndex(reader); - m_Snapshot.ReadAcks(clientId, m_ClientData[clientId], reader, GetConnectionRtt(clientId)); - m_Snapshot.ReadSpawns(reader); - - sentinel = reader.ReadUInt16(); - if (sentinel != SentinelAfter) - { - Debug.Log("Critical : snapshot integrity (after)"); - } + m_Snapshot.ReadBuffer(message); + m_Snapshot.ReadIndex(message); + m_Snapshot.ReadAcks(clientId, m_ClientData[clientId], message, GetConnectionRtt(clientId)); + m_Snapshot.ReadSpawns(message); } // todo --M1-- diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index 09d829c474..a6149a494a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -1,3 +1,4 @@ +using Unity.Netcode.Messages; using UnityEngine; namespace Unity.Netcode @@ -56,18 +57,16 @@ private static void LogServer(string message, LogType logType) if (NetworkManager.Singleton != null && !NetworkManager.Singleton.IsServer && NetworkManager.Singleton.NetworkConfig.EnableNetworkLogs) { - var context = NetworkManager.Singleton.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ServerLog, NetworkDelivery.ReliableSequenced, new[] { NetworkManager.Singleton.ServerClientId }, NetworkUpdateLoop.UpdateStage); - if (context != null) + + var networkMessage = new ServerLogMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - nonNullContext.NetworkWriter.WriteByte((byte)logType); - nonNullContext.NetworkWriter.WriteStringPacked(message); - var size = bufferSizeCapture.StopMeasureSegment(); + LogType = logType, + Message = message + }; + var size = NetworkManager.Singleton.SendMessage(networkMessage, NetworkDelivery.ReliableFragmentedSequenced, + NetworkManager.Singleton.ServerClientId); - NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size); - } + NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size); } } @@ -75,7 +74,7 @@ private static void LogServer(string message, LogType logType) internal static void LogWarningServerLocal(string message, ulong sender) => Debug.LogWarning($"[Netcode-Server Sender={sender}] {message}"); internal static void LogErrorServerLocal(string message, ulong sender) => Debug.LogError($"[Netcode-Server Sender={sender}] {message}"); - internal enum LogType + internal enum LogType : byte { Info, Warning, diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs index 8cb49c8e44..3c82d76341 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs @@ -5,15 +5,9 @@ namespace Unity.Netcode internal interface IInternalMessageHandler { NetworkManager NetworkManager { get; } - void HandleAddObject(ulong clientId, Stream stream); - void HandleDestroyObject(ulong clientId, Stream stream); void HandleSceneEvent(ulong clientId, Stream stream); - void HandleChangeOwner(ulong clientId, Stream stream); - void HandleTimeSync(ulong clientId, Stream stream); - void HandleNetworkVariableDelta(ulong clientId, Stream stream); void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType); void HandleUnnamedMessage(ulong clientId, Stream stream); void HandleNamedMessage(ulong clientId, Stream stream); - void HandleNetworkLog(ulong clientId, Stream stream); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs index 6ea6157e23..6351f1ec3e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs @@ -5,9 +5,9 @@ namespace Unity.Netcode public interface INetworkHooks { void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery); - void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery); - void OnBeforeReceiveMessage(ulong senderId, Type messageType); - void OnAfterReceiveMessage(ulong senderId, Type messageType); + void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes); + void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes); + void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes); void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs index 935e7bd96d..493aabd475 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs @@ -14,60 +14,6 @@ public InternalMessageHandler(NetworkManager networkManager) m_NetworkManager = networkManager; } - public void HandleAddObject(ulong clientId, Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - var isPlayerObject = reader.ReadBool(); - var networkId = reader.ReadUInt64Packed(); - var ownerClientId = reader.ReadUInt64Packed(); - var hasParent = reader.ReadBool(); - ulong? parentNetworkId = null; - - if (hasParent) - { - parentNetworkId = reader.ReadUInt64Packed(); - } - - var softSync = reader.ReadBool(); - var prefabHash = reader.ReadUInt32Packed(); - - Vector3? pos = null; - Quaternion? rot = null; - if (reader.ReadBool()) - { - pos = new Vector3(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); - rot = Quaternion.Euler(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); - } - - var (isReparented, latestParent) = NetworkObject.ReadNetworkParenting(reader); - - var networkObject = NetworkManager.SpawnManager.CreateLocalNetworkObject(softSync, prefabHash, ownerClientId, parentNetworkId, pos, rot, isReparented); - networkObject.SetNetworkParenting(isReparented, latestParent); - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, networkId, softSync, isPlayerObject, ownerClientId, stream, true, false); - m_NetworkManager.NetworkMetrics.TrackObjectSpawnReceived(clientId, networkObject.NetworkObjectId, networkObject.name, stream.Length); - } - - public void HandleDestroyObject(ulong clientId, Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - ulong networkObjectId = reader.ReadUInt64Packed(); - - if (!NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var networkObject)) - { - // This is the same check and log message that happens inside OnDespawnObject, but we have to do it here - // while we still have access to the network ID, otherwise the log message will be less useful. - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Trying to destroy {nameof(NetworkObject)} #{networkObjectId} but it does not exist in {nameof(NetworkSpawnManager.SpawnedObjects)} anymore!"); - } - - return; - } - - m_NetworkManager.NetworkMetrics.TrackObjectDestroyReceived(clientId, networkObjectId, networkObject.name, stream.Length); - NetworkManager.SpawnManager.OnDespawnObject(networkObject, true); - } - /// /// Called for all Scene Management related events /// @@ -78,88 +24,6 @@ public void HandleSceneEvent(ulong clientId, Stream stream) NetworkManager.SceneManager.HandleSceneEvent(clientId, stream); } - public void HandleChangeOwner(ulong clientId, Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - ulong networkObjectId = reader.ReadUInt64Packed(); - ulong ownerClientId = reader.ReadUInt64Packed(); - - if (!NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var networkObject)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Trying to handle owner change but {nameof(NetworkObject)} #{networkObjectId} does not exist in {nameof(NetworkSpawnManager.SpawnedObjects)} anymore!"); - } - - return; - } - - if (networkObject.OwnerClientId == NetworkManager.LocalClientId) - { - //We are current owner. - networkObject.InvokeBehaviourOnLostOwnership(); - } - - networkObject.OwnerClientId = ownerClientId; - - if (ownerClientId == NetworkManager.LocalClientId) - { - //We are new owner. - networkObject.InvokeBehaviourOnGainedOwnership(); - } - - NetworkManager.NetworkMetrics.TrackOwnershipChangeReceived(clientId, networkObject.NetworkObjectId, networkObject.name, stream.Length); - } - - public void HandleTimeSync(ulong clientId, Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - int tick = reader.ReadInt32Packed(); - var time = new NetworkTime(NetworkManager.NetworkTickSystem.TickRate, tick); - NetworkManager.NetworkTimeSystem.Sync(time.Time, NetworkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(clientId) / 1000d); - } - - public void HandleNetworkVariableDelta(ulong clientId, Stream stream) - { - if (!NetworkManager.NetworkConfig.EnableNetworkVariable) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Network variable delta received but {nameof(NetworkConfig.EnableNetworkVariable)} is false"); - } - - return; - } - - using var reader = PooledNetworkReader.Get(stream); - ulong networkObjectId = reader.ReadUInt64Packed(); - ushort networkBehaviourIndex = reader.ReadUInt16Packed(); - - if (NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - NetworkBehaviour behaviour = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourIndex); - - if (behaviour == null) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Network variable delta message received for a non-existent behaviour. {nameof(networkObjectId)}: {networkObjectId}, {nameof(networkBehaviourIndex)}: {networkBehaviourIndex}"); - } - } - else - { - behaviour.HandleNetworkVariableDeltas(stream, clientId); - } - } - else if (NetworkManager.IsServer) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Network variable delta message received for a non-existent object with {nameof(networkObjectId)}: {networkObjectId}. This delta was lost."); - } - } - } - /// /// Converts the stream to a PerformanceQueueItem and adds it to the receive queue /// @@ -211,32 +75,5 @@ public void HandleNamedMessage(ulong clientId, Stream stream) NetworkManager.CustomMessagingManager.InvokeNamedMessage(hash, clientId, stream); } - - public void HandleNetworkLog(ulong clientId, Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - var length = stream.Length; - var logType = (NetworkLog.LogType)reader.ReadByte(); - m_NetworkManager.NetworkMetrics.TrackServerLogReceived(clientId, (uint)logType, length); - string message = reader.ReadStringPacked(); - - switch (logType) - { - case NetworkLog.LogType.Info: - NetworkLog.LogInfoServerLocal(message, clientId); - break; - case NetworkLog.LogType.Warning: - NetworkLog.LogWarningServerLocal(message, clientId); - break; - case NetworkLog.LogType.Error: - NetworkLog.LogErrorServerLocal(message, clientId); - break; - } - } - - internal static void HandleSnapshot(ulong clientId, Stream messageStream) - { - NetworkManager.Singleton.SnapshotSystem.ReadSnapshot(clientId, messageStream); - } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs index 4cc58a38e8..a03dd19f35 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs @@ -21,17 +21,10 @@ public enum MessageType { ClientRpc, ServerRpc, - CreateObject, - DestroyObject, - ChangeOwner, - TimeSync, UnnamedMessage, NamedMessage, - ServerLog, SnapshotData, - NetworkVariableDelta, SceneEvent, - ParentSync, None //Indicates end of frame } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs index 381e980bb7..16be7876f2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs @@ -49,75 +49,15 @@ public void ProcessMessage(in MessageFrameItem item) // After all, that's the whole point of it being in the buffer. m_NetworkManager.InvokeRpc(item, item.UpdateStage); break; - case MessageQueueContainer.MessageType.CreateObject: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleAddObject(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.DestroyObject: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleDestroyObject(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.ChangeOwner: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleChangeOwner(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.TimeSync: - if (m_NetworkManager.IsClient) - { - m_NetworkManager.MessageHandler.HandleTimeSync(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.UnnamedMessage: m_NetworkManager.MessageHandler.HandleUnnamedMessage(item.NetworkId, item.NetworkBuffer); break; case MessageQueueContainer.MessageType.NamedMessage: m_NetworkManager.MessageHandler.HandleNamedMessage(item.NetworkId, item.NetworkBuffer); break; - case MessageQueueContainer.MessageType.ServerLog: - if (m_NetworkManager.IsServer && m_NetworkManager.NetworkConfig.EnableNetworkLogs) - { - m_NetworkManager.MessageHandler.HandleNetworkLog(item.NetworkId, item.NetworkBuffer); - } - - break; - case MessageQueueContainer.MessageType.SnapshotData: - InternalMessageHandler.HandleSnapshot(item.NetworkId, item.NetworkBuffer); - break; - case MessageQueueContainer.MessageType.NetworkVariableDelta: - m_NetworkManager.MessageHandler.HandleNetworkVariableDelta(item.NetworkId, item.NetworkBuffer); - break; case MessageQueueContainer.MessageType.SceneEvent: m_NetworkManager.MessageHandler.HandleSceneEvent(item.NetworkId, item.NetworkBuffer); break; - case MessageQueueContainer.MessageType.ParentSync: - if (m_NetworkManager.IsClient) - { - var networkObjectId = item.NetworkReader.ReadUInt64Packed(); - var (isReparented, latestParent) = NetworkObject.ReadNetworkParenting(item.NetworkReader); - if (m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) - { - var networkObject = m_NetworkManager.SpawnManager.SpawnedObjects[networkObjectId]; - networkObject.SetNetworkParenting(isReparented, latestParent); - networkObject.ApplyNetworkParenting(); - } - else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogWarning($"Read {item.MessageType} for {nameof(NetworkObject)} #{networkObjectId} but could not find it in the {nameof(m_NetworkManager.SpawnManager.SpawnedObjects)}"); - } - } - - break; default: NetworkLog.LogWarning($"Received unknown message {((int)item.MessageType).ToString()}"); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs new file mode 100644 index 0000000000..02de971d14 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -0,0 +1,53 @@ +namespace Unity.Netcode.Messages +{ + internal struct ChangeOwnershipMessage: INetworkMessage + { + public ulong NetworkObjectId; + public ulong OwnerClientId; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsClient) + { + return; + } + reader.ReadValue(out ChangeOwnershipMessage message); + message.Handle(context.SenderId, networkManager, reader.Length); + } + + public void Handle(ulong senderId, NetworkManager networkManager, int messageSize) + { + if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Trying to handle owner change but {nameof(NetworkObject)} #{NetworkObjectId} does not exist in {nameof(NetworkSpawnManager.SpawnedObjects)} anymore!"); + } + + return; + } + + if (networkObject.OwnerClientId == networkManager.LocalClientId) + { + //We are current owner. + networkObject.InvokeBehaviourOnLostOwnership(); + } + + networkObject.OwnerClientId = OwnerClientId; + + if (OwnerClientId == networkManager.LocalClientId) + { + //We are new owner. + networkObject.InvokeBehaviourOnGainedOwnership(); + } + + networkManager.NetworkMetrics.TrackOwnershipChangeReceived(senderId, networkObject.NetworkObjectId, networkObject.name, messageSize); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs.meta new file mode 100644 index 0000000000..a99599609e --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 841becdc46b20d5408a81bc30ac950f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs index b01522e867..a22733327e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; @@ -9,7 +10,9 @@ internal struct ConnectionApprovedMessage: INetworkMessage public ulong OwnerClientId; public int NetworkTick; public int SceneObjectCount; - public NativeArray SceneObjects; + + // Not serialized, held as references to serialize NetworkVariable data + public HashSet SpawnedObjectsList; public void Serialize(ref FastBufferWriter writer) { @@ -21,41 +24,44 @@ public void Serialize(ref FastBufferWriter writer) writer.WriteValue(OwnerClientId); writer.WriteValue(NetworkTick); writer.WriteValue(SceneObjectCount); - if (SceneObjectCount != 0) + + if(SceneObjectCount != 0) { - foreach (var sceneObject in SceneObjects) + // Serialize NetworkVariable data + foreach (var sobj in SpawnedObjectsList) { - sceneObject.Serialize(ref writer); + if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(OwnerClientId)) + { + sobj.Observers.Add(OwnerClientId); + var sceneObject = sobj.GetMessageSceneObject(OwnerClientId); + sceneObject.Serialize(ref writer); + } } - - SceneObjects.Dispose(); } } public static void Receive(ref FastBufferReader reader, NetworkContext context) { - if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int))) + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsClient) { return; } + + if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int))) + { + throw new OverflowException( + $"Not enough space in the buffer to read {nameof(ConnectionApprovedMessage)}"); + } var message = new ConnectionApprovedMessage(); reader.ReadValue(out message.OwnerClientId); reader.ReadValue(out message.NetworkTick); reader.ReadValue(out message.SceneObjectCount); - message.SceneObjects = new NativeArray(message.SceneObjectCount, Allocator.Temp); - using (message.SceneObjects) - { - for (var i = 0; i < message.SceneObjectCount; ++i) - { - message.SceneObjects[i] = new NetworkObject.SceneObject(); - message.SceneObjects[i].Deserialize(ref reader); - } - } - message.Handle(context.SenderId, (NetworkManager)context.SystemOwner); + message.Handle(ref reader, context.SenderId, networkManager); } - public unsafe void Handle(ulong clientId, NetworkManager networkManager) + public void Handle(ref FastBufferReader reader, ulong clientId, NetworkManager networkManager) { networkManager.LocalClientId = OwnerClientId; @@ -69,10 +75,13 @@ public unsafe void Handle(ulong clientId, NetworkManager networkManager) { networkManager.SpawnManager.DestroySceneObjects(); - NetworkObject.SceneObject* ptr = (NetworkObject.SceneObject*)SceneObjects.GetUnsafePtr(); + // Deserializing NetworkVariable data is deferred from Receive() to Handle to avoid needing + // to create a list to hold the data. This is a breach of convention for performance reasons. for (ushort i = 0; i < SceneObjectCount; i++) { - NetworkObject.AddSceneObject(ref ptr[i], networkManager); + var sceneObject = new NetworkObject.SceneObject(); + sceneObject.Deserialize(ref reader); + NetworkObject.AddSceneObject(sceneObject, ref reader, networkManager); } // Mark the client being connected diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs index 53f28bece9..e7b1616d7a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs @@ -47,6 +47,10 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsServer) + { + return; + } ConnectionRequestMessage message = new ConnectionRequestMessage(); if (networkManager.NetworkConfig.ConnectionApproval) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs new file mode 100644 index 0000000000..d946c85daf --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -0,0 +1,32 @@ +using UnityEngine; + +namespace Unity.Netcode.Messages +{ + internal struct CreateObjectMessage : INetworkMessage + { + public NetworkObject.SceneObject ObjectInfo; + + public void Serialize(ref FastBufferWriter writer) + { + ObjectInfo.Serialize(ref writer); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsClient) + { + return; + } + var message = new CreateObjectMessage(); + message.ObjectInfo.Deserialize(ref reader); + message.Handle(context.SenderId, ref reader, networkManager); + } + + public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager networkManager) + { + var networkObject = NetworkObject.AddSceneObject(ObjectInfo, ref reader, networkManager); + networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject.NetworkObjectId, networkObject.name, reader.Length); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs.meta new file mode 100644 index 0000000000..1fa4fb6a31 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff075de988adf5a4294620aa2d85fcd0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs new file mode 100644 index 0000000000..af51eb0c60 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs @@ -0,0 +1,41 @@ +namespace Unity.Netcode.Messages +{ + internal struct DestroyObjectMessage : INetworkMessage + { + public ulong NetworkObjectId; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValueSafe(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsClient) + { + return; + } + reader.ReadValueSafe(out DestroyObjectMessage message); + message.Handle(context.SenderId, networkManager, reader.Length); + } + + public void Handle(ulong senderId, NetworkManager networkManager, int messageSize) + { + if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject)) + { + // This is the same check and log message that happens inside OnDespawnObject, but we have to do it here + // while we still have access to the network ID, otherwise the log message will be less useful. + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Trying to destroy {nameof(NetworkObject)} #{NetworkObjectId} but it does not exist in {nameof(NetworkSpawnManager.SpawnedObjects)} anymore!"); + } + + return; + } + + networkManager.NetworkMetrics.TrackObjectDestroyReceived(senderId, NetworkObjectId, networkObject.name, messageSize); + networkManager.SpawnManager.OnDespawnObject(networkObject, true); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs.meta new file mode 100644 index 0000000000..35e4b0468d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18473ed11c97e7241aecb0c72d4bffb2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs new file mode 100644 index 0000000000..b256dd3c6a --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using Unity.Collections; + +namespace Unity.Netcode.Messages +{ + /// + /// This particular struct is a little weird because it doesn't actually contain the data + /// it's serializing. Instead, it contains references to the data it needs to do the + /// serialization. This is due to the generally amorphous nature of network variable + /// deltas, since they're all driven by custom virtual method overloads. + /// + internal struct NetworkVariableDeltaMessage : INetworkMessage + { + public ulong NetworkObjectId; + public ushort NetworkBehaviourIndex; + + public HashSet DeliveryMappedNetworkVariableIndex; + public ulong ClientId; + public NetworkBehaviour NetworkBehaviour; + + public void Serialize(ref FastBufferWriter writer) + { + if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex))) + { + throw new OverflowException( + $"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}"); + } + writer.WriteValue(NetworkObjectId); + writer.WriteValue(NetworkBehaviourIndex); + for (int k = 0; k < NetworkBehaviour.NetworkVariableFields.Count; k++) + { + if (!DeliveryMappedNetworkVariableIndex.Contains(k)) + { + // This var does not belong to the currently iterating delivery group. + if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + writer.WriteValueSafe((short) 0); + } + else + { + writer.WriteValueSafe(false); + } + + continue; + } + + // if I'm dirty AND a client, write (server always has all permissions) + // if I'm dirty AND the server AND the client can read me, send. + bool shouldWrite = NetworkBehaviour.NetworkVariableFields[k].ShouldWrite(ClientId, NetworkBehaviour.NetworkManager.IsServer); + + if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + if (!shouldWrite) + { + writer.WriteValueSafe((ushort) 0); + } + } + else + { + writer.WriteValueSafe(shouldWrite); + } + + if (shouldWrite) + { + if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + var tmpWriter = new FastBufferWriter(1300, Allocator.Temp, Int16.MaxValue); + NetworkBehaviour.NetworkVariableFields[k].WriteDelta(ref tmpWriter); + + writer.WriteValueSafe((ushort)tmpWriter.Length); + tmpWriter.CopyTo(ref writer); + } + else + { + NetworkBehaviour.NetworkVariableFields[k].WriteDelta(ref writer); + } + + if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(k)) + { + NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(k); + NetworkBehaviour.NetworkVariableIndexesToReset.Add(k); + } + + NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent( + ClientId, + NetworkBehaviour.NetworkObjectId, + NetworkBehaviour.name, + NetworkBehaviour.NetworkVariableFields[k].Name, + NetworkBehaviour.__getTypeName(), + writer.Length); + } + } + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.NetworkConfig.EnableNetworkVariable) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning( + $"Network variable delta received but {nameof(NetworkConfig.EnableNetworkVariable)} is false"); + } + + return; + } + + var message = new NetworkVariableDeltaMessage(); + reader.ReadValue(out message.NetworkObjectId); + reader.ReadValue(out message.NetworkBehaviourIndex); + message.Handle(context.SenderId, ref reader, networkManager); + } + + public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager networkManager) + { + if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject)) + { + NetworkBehaviour behaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex); + + if (behaviour == null) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Network variable delta message received for a non-existent behaviour. {nameof(NetworkObjectId)}: {NetworkObjectId}, {nameof(NetworkBehaviourIndex)}: {NetworkBehaviourIndex}"); + } + } + else + { + for (int i = 0; i < behaviour.NetworkVariableFields.Count; i++) + { + ushort varSize = 0; + + if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + reader.ReadValue(out varSize); + + if (varSize == 0) + { + continue; + } + } + else + { + reader.ReadValue(out bool deltaExists); + if (!deltaExists) + { + continue; + } + } + + if (networkManager.IsServer && !behaviour.NetworkVariableFields[i].CanClientWrite(senderId)) + { + // we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server + if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}"); + NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]"); + } + + reader.Seek(reader.Position + varSize); + continue; + } + + //This client wrote somewhere they are not allowed. This is critical + //We can't just skip this field. Because we don't actually know how to dummy read + //That is, we don't know how many bytes to skip. Because the interface doesn't have a + //Read that gives us the value. Only a Read that applies the value straight away + //A dummy read COULD be added to the interface for this situation, but it's just being too nice. + //This is after all a developer fault. A critical error should be fine. + // - TwoTen + if (NetworkLog.CurrentLogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}"); + NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]"); + } + + return; + } + int readStartPos = reader.Position; + + behaviour.NetworkVariableFields[i].ReadDelta(ref reader, networkManager.IsServer); + networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived( + senderId, + behaviour.NetworkObjectId, + behaviour.name, + behaviour.NetworkVariableFields[i].Name, + behaviour.__getTypeName(), + reader.Length); + + + if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + if (reader.Position > (readStartPos + varSize)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning( + $"Var delta read too far. {reader.Position - (readStartPos + varSize)} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}"); + } + + reader.Seek(readStartPos + varSize); + } + else if (reader.Position < (readStartPos + varSize)) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning( + $"Var delta read too little. {(readStartPos + varSize) - reader.Position} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}"); + } + + reader.Seek(readStartPos + varSize); + } + } + } + } + } + else if (networkManager.IsServer) + { + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Network variable delta message received for a non-existent object with {nameof(NetworkObjectId)}: {NetworkObjectId}. This delta was lost."); + } + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs.meta new file mode 100644 index 0000000000..3593f1a23c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfa1a454cc9fdb647ba89479fa6b8299 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs new file mode 100644 index 0000000000..2e3f5b7040 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs @@ -0,0 +1,69 @@ +namespace Unity.Netcode.Messages +{ + public struct ParentSyncMessage : INetworkMessage + { + public ulong NetworkObjectId; + + public bool IsReparented; + + #region If(Metadata.IsReparented) + public bool IsLatestParentSet; + + #region If(IsLatestParentSet) + public ulong? LatestParent; + #endregion + #endregion + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(NetworkObjectId); + writer.WriteValue(IsReparented); + if (IsReparented) + { + writer.WriteValue(IsLatestParentSet); + if (IsLatestParentSet) + { + writer.WriteValue((ulong)LatestParent); + } + } + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsClient) + { + return; + } + + var message = new ParentSyncMessage(); + reader.ReadValueSafe(out message.NetworkObjectId); + reader.ReadValueSafe(out message.IsReparented); + if (message.IsReparented) + { + reader.ReadValueSafe(out message.IsLatestParentSet); + if (message.IsLatestParentSet) + { + reader.ReadValueSafe(out ulong latestParent); + message.LatestParent = latestParent; + } + } + + message.Handle(networkManager); + } + + public void Handle(NetworkManager networkManager) + { + if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId)) + { + var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId]; + networkObject.SetNetworkParenting(IsReparented, LatestParent); + networkObject.ApplyNetworkParenting(); + } + else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"Read {nameof(ParentSyncMessage)} for {nameof(NetworkObject)} #{NetworkObjectId} but could not find it in the {nameof(networkManager.SpawnManager.SpawnedObjects)}"); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs.meta new file mode 100644 index 0000000000..2b019784f2 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 237bfa46868f1ff48863f3a6df2f5506 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs new file mode 100644 index 0000000000..2c8f9aeefa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs @@ -0,0 +1,53 @@ +using System; + +namespace Unity.Netcode.Messages +{ + internal struct ServerLogMessage : INetworkMessage + { + public NetworkLog.LogType LogType; + // It'd be lovely to be able to replace this with FixedUnmanagedArray... + // But it's not really practical. On the sending side, the user is likely to want + // to work with strings and would need to convert, and on the receiving side, + // we'd have to convert it to a string to be able to pass it to the log system. + // So an allocation is unavoidable here on both sides. + public string Message; + + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValueSafe(LogType); + BytePacker.WriteValuePacked(ref writer, Message); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs) + { + var message = new ServerLogMessage(); + reader.ReadValueSafe(out message.LogType); + ByteUnpacker.ReadValuePacked(ref reader, out message.Message); + message.Handle(context.SenderId, networkManager, reader.Length); + } + } + + public void Handle(ulong senderId, NetworkManager networkManager, int messageSize) + { + + networkManager.NetworkMetrics.TrackServerLogReceived(senderId, (uint)LogType, messageSize); + + switch (LogType) + { + case NetworkLog.LogType.Info: + NetworkLog.LogInfoServerLocal(Message, senderId); + break; + case NetworkLog.LogType.Warning: + NetworkLog.LogWarningServerLocal(Message, senderId); + break; + case NetworkLog.LogType.Error: + NetworkLog.LogErrorServerLocal(Message, senderId); + break; + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs.meta new file mode 100644 index 0000000000..ebc461d1f1 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d1c8aae1f7b7194eb3ab1cab260f34f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs new file mode 100644 index 0000000000..f04669c2cd --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs @@ -0,0 +1,127 @@ +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine; + +namespace Unity.Netcode.Messages +{ + internal struct SnapshotDataMessage: INetworkMessage + { + public int CurrentTick; + public ushort Sequence; + + public ushort Range; + + public byte[] SendMainBuffer; + public NativeArray ReceiveMainBuffer; + + public struct AckData + { + public ushort LastReceivedSequence; + public ushort ReceivedSequenceMask; + } + + public AckData Ack; + + public struct EntryData + { + public ulong NetworkObjectId; + public ushort BehaviourIndex; + public ushort VariableIndex; + public int TickWritten; + public ushort Position; + public ushort Length; + } + + public NativeArray Entries; + + public struct SpawnData + { + public ulong NetworkObjectId; + public uint Hash; + public bool IsSceneObject; + + public bool IsPlayerObject; + public ulong OwnerClientId; + public ulong ParentNetworkId; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Scale; + + public int TickWritten; + } + + public NativeArray Spawns; + + public struct DespawnData + { + public ulong NetworkObjectId; + public int TickWritten; + } + + public NativeArray Despawns; + + public unsafe void Serialize(ref FastBufferWriter writer) + { + writer.WriteValue(CurrentTick); + writer.WriteValue(Sequence); + + writer.WriteValue(Range); + writer.WriteBytes(SendMainBuffer, Range); + writer.WriteValue(Ack); + + writer.WriteValue((ushort)Entries.Length); + writer.WriteBytes((byte*)Entries.GetUnsafePtr(), Entries.Length * sizeof(EntryData)); + + writer.WriteValue((ushort)Spawns.Length); + writer.WriteBytes((byte*)Spawns.GetUnsafePtr(), Spawns.Length * sizeof(SpawnData)); + + writer.WriteValue((ushort)Despawns.Length); + writer.WriteBytes((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData)); + } + + public static unsafe void Receive(ref FastBufferReader reader, NetworkContext context) + { + NetworkManager networkManager = (NetworkManager) context.SystemOwner; + var message = new SnapshotDataMessage(); + reader.ReadValue(out message.CurrentTick); + reader.ReadValue(out message.Sequence); + + reader.ReadValue(out message.Range); + message.ReceiveMainBuffer = new NativeArray(message.Range, Allocator.Temp); + reader.ReadBytes((byte*)message.ReceiveMainBuffer.GetUnsafePtr(), message.Range); + reader.ReadValue(out message.Ack); + + reader.ReadValue(out ushort length); + message.Entries = new NativeArray(length, Allocator.Temp); + reader.ReadBytes((byte*)message.Entries.GetUnsafePtr(), message.Entries.Length * sizeof(EntryData)); + + reader.ReadValue(out length); + message.Spawns = new NativeArray(length, Allocator.Temp); + reader.ReadBytes((byte*)message.Spawns.GetUnsafePtr(), message.Spawns.Length * sizeof(SpawnData)); + + reader.ReadValue(out length); + message.Despawns = new NativeArray(length, Allocator.Temp); + reader.ReadBytes((byte*)message.Despawns.GetUnsafePtr(), message.Despawns.Length * sizeof(DespawnData)); + + using (message.ReceiveMainBuffer) + using (message.Entries) + using (message.Spawns) + using (message.Despawns) + { + message.Handle(context.SenderId, networkManager); + } + } + + public void Handle(ulong senderId, NetworkManager networkManager) + { + // todo: temporary hack around bug + if (!networkManager.IsServer) + { + senderId = networkManager.ServerClientId; + } + + var snapshotSystem = networkManager.SnapshotSystem; + snapshotSystem.HandleSnapshot(senderId, this); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs.meta new file mode 100644 index 0000000000..a549a7c571 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5cf75026c2ab86646aac16b39d7259ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs new file mode 100644 index 0000000000..0ff672e71d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs @@ -0,0 +1,31 @@ +using AOT; + +namespace Unity.Netcode.Messages +{ + internal struct TimeSyncMessage : INetworkMessage + { + public int Tick; + + public void Serialize(ref FastBufferWriter writer) + { + writer.WriteValueSafe(this); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var networkManager = (NetworkManager) context.SystemOwner; + if (!networkManager.IsClient) + { + return; + } + reader.ReadValueSafe(out TimeSyncMessage message); + message.Handle(context.SenderId, networkManager); + } + + public void Handle(ulong senderId, NetworkManager networkManager) + { + var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, Tick); + networkManager.NetworkTimeSystem.Sync(time.Time, networkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(senderId) / 1000d); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs.meta new file mode 100644 index 0000000000..8c64b06ba3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94afa081c8f5e0a4fb05e0643a968c46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index a82954afe4..ed05a90705 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -185,14 +185,14 @@ private void RegisterMessageType(Type messageType) if (method == null) { throw new InvalidMessageStructureException( - "All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); + $"{messageType.Name}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); } var asDelegate = Delegate.CreateDelegate(typeof(MessageHandler), method, false); if (asDelegate == null) { throw new InvalidMessageStructureException( - "All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); + $"{messageType.Name}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); } m_MessageHandlers[m_HighMessageType] = (MessageHandler) asDelegate; @@ -223,7 +223,7 @@ internal void HandleIncomingData(ulong clientId, ArraySegment data, float for (var messageIdx = 0; messageIdx < batchHeader.BatchSize; ++messageIdx) { - Debug.Log("Receiving a batch"); + Debug.Log($"Receiving a batch: {BitConverter.ToString(batchReader.ToArray())}"); if (!batchReader.TryBeginRead(sizeof(MessageHeader))) { NetworkLog.LogWarning("Received a batch that didn't have enough data for all of its batches, ending early!"); @@ -286,9 +286,9 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type); + m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length); } - Debug.Log($"Processing {type}"); + Debug.Log($"Processing {type} ({header.MessageType}): {BitConverter.ToString(reader.ToArray())}"); var handler = m_MessageHandlers[header.MessageType]; using (reader) { @@ -308,7 +308,7 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, } for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnAfterReceiveMessage(senderId, type); + m_Hooks[hookIdx].OnAfterReceiveMessage(senderId, type, reader.Length); } } @@ -354,7 +354,7 @@ private bool CanSend(ulong clientId, Type messageType, NetworkDelivery delivery) return true; } - internal unsafe void SendMessage(in T message, NetworkDelivery delivery, in U clientIds) + internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds) where T: INetworkMessage where U: IReadOnlyList { @@ -378,7 +378,7 @@ internal unsafe void SendMessage(in T message, NetworkDelivery delivery, i m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(T), delivery); } - Debug.Log($"Sending {typeof(T)} to {clientId}"); + Debug.Log($"Sending {typeof(T)} ({m_MessageTypes[typeof(T)]}) to {clientId}: {BitConverter.ToString(tmpSerializer.ToArray())}"); ref var sendQueueItem = ref m_SendQueues[clientId].Value; if (sendQueueItem.Count == 0) @@ -395,7 +395,7 @@ internal unsafe void SendMessage(in T message, NetworkDelivery delivery, i { sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, maxSize)); - sendQueueItem.GetValueRef(0).Writer.Seek(sizeof(BatchHeader)); + sendQueueItem.GetValueRef(sendQueueItem.Count - 1).Writer.Seek(sizeof(BatchHeader)); } } @@ -425,10 +425,13 @@ internal unsafe void SendMessage(in T message, NetworkDelivery delivery, i writeQueueItem.BatchHeader.BatchSize++; for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(T), delivery); + m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(T), delivery, tmpSerializer.Length + sizeof(MessageHeader)); } Debug.Log($"Sented {typeof(T)} to {clientId} batch size is now {writeQueueItem.BatchHeader.BatchSize}"); + Debug.Log(BitConverter.ToString(writeQueueItem.Writer.ToArray())); } + + return tmpSerializer.Length; } } @@ -467,19 +470,19 @@ IEnumerator IEnumerable.GetEnumerator() } } - internal unsafe void SendMessage(in T message, NetworkDelivery delivery, + internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong* clientIds, int numClientIds) where T: INetworkMessage { - SendMessage(message, delivery, new PointerListWrapper(clientIds, numClientIds)); + return SendMessage(message, delivery, new PointerListWrapper(clientIds, numClientIds)); } - internal unsafe void SendMessage(in T message, NetworkDelivery delivery, + internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong clientId) where T: INetworkMessage { ulong* clientIds = stackalloc ulong[] {clientId}; - SendMessage(message, delivery, new PointerListWrapper(clientIds, 1)); + return SendMessage(message, delivery, new PointerListWrapper(clientIds, 1)); } internal void ProcessSendQueues() @@ -496,7 +499,6 @@ internal void ProcessSendQueues() queueItem.Writer.Dispose(); continue; } - Debug.Log($"Sending a batch to {clientId}"); for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { @@ -507,13 +509,14 @@ internal void ProcessSendQueues() // Skipping the Verify and sneaking the write mark in because we know it's fine. queueItem.Writer.AllowedWriteMark = 2; queueItem.Writer.WriteValue(queueItem.BatchHeader); + Debug.Log($"Sending a batch to {clientId}: {BitConverter.ToString(queueItem.Writer.ToArray())}"); m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); queueItem.Writer.Dispose(); for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnBeforeSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); + m_Hooks[hookIdx].OnAfterSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); } } sendQueueItem.Clear(); diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs new file mode 100644 index 0000000000..0f473f436d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Unity.Profiling; + +namespace Unity.Netcode +{ + internal class MetricHooks : INetworkHooks + { + private readonly NetworkManager m_NetworkManager; + + public MetricHooks(NetworkManager networkManager) + { + m_NetworkManager = networkManager; + } + + + public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) + { + } + + public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes) + { + m_NetworkManager.NetworkMetrics.TrackNetworkMessageSent(clientId, messageType.Name, messageSizeBytes); + } + + public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes) + { + m_NetworkManager.NetworkMetrics.TrackNetworkMessageReceived(senderId, messageType.Name, messageSizeBytes); + } + + public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes) + { + } + + public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery) + { + } + + public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery) + { + m_NetworkManager.NetworkMetrics.TrackTransportBytesSent(batchSizeInBytes); + } + + public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes) + { + m_NetworkManager.NetworkMetrics.TrackTransportBytesReceived(batchSizeInBytes); + } + + public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes) + { + } + + public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery) + { + return true; + } + + public bool OnVerifyCanReceive(ulong senderId, Type messageType) + { + return true; + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs.meta b/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs.meta new file mode 100644 index 0000000000..2ad0052672 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07e749f617714f248823af11048bfe46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs b/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs index 422a3015b3..bf3bab1500 100644 --- a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs @@ -5,15 +5,9 @@ namespace Unity.Netcode { internal class InternalMessageHandlerProfilingDecorator : IInternalMessageHandler { - private readonly ProfilerMarker m_HandleAddObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAddObject)}"); - private readonly ProfilerMarker m_HandleDestroyObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleDestroyObject)}"); private readonly ProfilerMarker m_HandleSceneEvent = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleSceneEvent)}"); - private readonly ProfilerMarker m_HandleChangeOwner = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleChangeOwner)}"); - private readonly ProfilerMarker m_HandleTimeSync = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleTimeSync)}"); - private readonly ProfilerMarker m_HandleNetworkVariableDelta = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNetworkVariableDelta)}"); private readonly ProfilerMarker m_HandleUnnamedMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleUnnamedMessage)}"); private readonly ProfilerMarker m_HandleNamedMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNamedMessage)}"); - private readonly ProfilerMarker m_HandleNetworkLog = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNetworkLog)}"); private readonly ProfilerMarker m_MessageReceiveQueueItemServerRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.{nameof(MessageQueueContainer.MessageType.ServerRpc)}"); private readonly ProfilerMarker m_MessageReceiveQueueItemClientRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.{nameof(MessageQueueContainer.MessageType.ClientRpc)}"); private readonly ProfilerMarker m_MessageReceiveQueueItemInternalMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.InternalMessage"); @@ -27,52 +21,6 @@ internal InternalMessageHandlerProfilingDecorator(IInternalMessageHandler messag public NetworkManager NetworkManager => m_MessageHandler.NetworkManager; - public void HandleAddObject(ulong clientId, Stream stream) - { - m_HandleAddObject.Begin(); - - m_MessageHandler.HandleAddObject(clientId, stream); - - m_HandleAddObject.End(); - } - - public void HandleDestroyObject(ulong clientId, Stream stream) - { - m_HandleDestroyObject.Begin(); - - m_MessageHandler.HandleDestroyObject(clientId, stream); - - m_HandleDestroyObject.End(); - } - - - public void HandleChangeOwner(ulong clientId, Stream stream) - { - m_HandleChangeOwner.Begin(); - - m_MessageHandler.HandleChangeOwner(clientId, stream); - - m_HandleChangeOwner.End(); - } - - public void HandleTimeSync(ulong clientId, Stream stream) - { - m_HandleTimeSync.Begin(); - - m_MessageHandler.HandleTimeSync(clientId, stream); - - m_HandleTimeSync.End(); - } - - public void HandleNetworkVariableDelta(ulong clientId, Stream stream) - { - m_HandleNetworkVariableDelta.Begin(); - - m_MessageHandler.HandleNetworkVariableDelta(clientId, stream); - - m_HandleNetworkVariableDelta.End(); - } - public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType) { switch (messageType) @@ -122,15 +70,6 @@ public void HandleNamedMessage(ulong clientId, Stream stream) m_HandleNamedMessage.End(); } - public void HandleNetworkLog(ulong clientId, Stream stream) - { - m_HandleNetworkLog.Begin(); - - m_MessageHandler.HandleNetworkLog(clientId, stream); - - m_HandleNetworkLog.End(); - } - public void HandleSceneEvent(ulong clientId, Stream stream) { m_HandleSceneEvent.Begin(); diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs b/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs new file mode 100644 index 0000000000..5637b3d913 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Unity.Profiling; + +namespace Unity.Netcode +{ + internal class ProfilingHooks : INetworkHooks + { + private Dictionary m_handlerProfilerMarkers = new Dictionary(); + private Dictionary m_senderProfilerMarkers = new Dictionary(); + private readonly ProfilerMarker m_SendBatch = new ProfilerMarker($"{nameof(MessagingSystem)}.SendBatch"); + private readonly ProfilerMarker m_ReceiveBatch = new ProfilerMarker($"{nameof(MessagingSystem)}.ReceiveBatchBatch"); + + private ProfilerMarker GetHandlerProfilerMarker(Type type) + { + var result = m_handlerProfilerMarkers.TryGetValue(type, out var marker); + if (result) + { + return marker; + } + + marker = new ProfilerMarker($"{nameof(MessagingSystem)}.DeserializeAndHandle.{type.Name}"); + m_handlerProfilerMarkers[type] = marker; + return marker; + } + + private ProfilerMarker GetSenderProfilerMarker(Type type) + { + var result = m_senderProfilerMarkers.TryGetValue(type, out var marker); + if (result) + { + return marker; + } + + marker = new ProfilerMarker($"{nameof(MessagingSystem)}.SerializeAndEnqueue.{type.Name}"); + m_senderProfilerMarkers[type] = marker; + return marker; + } + + public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) + { + GetSenderProfilerMarker(messageType).Begin(); + } + + public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes) + { + GetSenderProfilerMarker(messageType).End(); + } + + public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes) + { + GetHandlerProfilerMarker(messageType).Begin(); + } + + public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes) + { + GetHandlerProfilerMarker(messageType).End(); + } + + public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery) + { + m_SendBatch.Begin(); + } + + public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery) + { + m_SendBatch.End(); + } + + public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes) + { + m_ReceiveBatch.Begin(); + } + + public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes) + { + m_ReceiveBatch.End(); + } + + public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery) + { + return true; + } + + public bool OnVerifyCanReceive(ulong senderId, Type messageType) + { + return true; + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs.meta b/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs.meta new file mode 100644 index 0000000000..b30d52e826 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da46509a8ea6d1c4390e0102f6422785 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index ead62a5dd1..7c3096dcf3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -104,22 +104,16 @@ internal void RemoveOwnership(NetworkObject networkObject) networkObject.OwnerClientIdInternal = null; - var context = NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ChangeOwner, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds, NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new ChangeOwnershipMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); + NetworkObjectId = networkObject.NetworkObjectId, + OwnerClientId = networkObject.OwnerClientId + }; + var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds); - nonNullContext.NetworkWriter.WriteUInt64Packed(networkObject.NetworkObjectId); - nonNullContext.NetworkWriter.WriteUInt64Packed(networkObject.OwnerClientId); - - var size = bufferSizeCapture.StopMeasureSegment(); - - foreach (var client in NetworkManager.ConnectedClients) - { - NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject.NetworkObjectId, networkObject.name, size); - } + foreach (var client in NetworkManager.ConnectedClients) + { + NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject.NetworkObjectId, networkObject.name, size); } } @@ -150,23 +144,17 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId) networkObject.OwnerClientId = clientId; - ulong[] clientIds = NetworkManager.ConnectedClientsIds; - var messageQueueContainer = NetworkManager.MessageQueueContainer; - var context = messageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.ChangeOwner, NetworkDelivery.ReliableSequenced, clientIds, NetworkUpdateLoop.UpdateStage); - if (context != null) + + var message = new ChangeOwnershipMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); + NetworkObjectId = networkObject.NetworkObjectId, + OwnerClientId = networkObject.OwnerClientId + }; + var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds); - nonNullContext.NetworkWriter.WriteUInt64Packed(networkObject.NetworkObjectId); - nonNullContext.NetworkWriter.WriteUInt64Packed(clientId); - - var size = bufferSizeCapture.StopMeasureSegment(); - foreach (var client in NetworkManager.ConnectedClients) - { - NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject.NetworkObjectId, networkObject.name, size); - } + foreach (var client in NetworkManager.ConnectedClients) + { + NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject.NetworkObjectId, networkObject.name, size); } } @@ -368,7 +356,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo } // Ran on both server and client - internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ref NetworkObject.SceneObject sceneObject, bool destroyWithScene) + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, ref FastBufferReader variableData, bool destroyWithScene) { if (networkObject == null) { @@ -382,7 +370,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ref Network if (sceneObject.Metadata.HasNetworkVariables && NetworkManager.NetworkConfig.EnableNetworkVariable) { - networkObject.SetNetworkVariableData(ref sceneObject.NetworkVariableDataReader); + networkObject.SetNetworkVariableData(ref variableData); } if (SpawnedObjects.ContainsKey(sceneObject.Metadata.NetworkObjectId)) @@ -451,20 +439,12 @@ internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject return; } - var messageQueueContainer = NetworkManager.MessageQueueContainer; - - var context = messageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.CreateObject, NetworkDelivery.ReliableSequenced, new ulong[] { clientId }, NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new CreateObjectMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - WriteSpawnCallForObject(nonNullContext.NetworkWriter, clientId, networkObject); - - var size = bufferSizeCapture.StopMeasureSegment(); - NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject.NetworkObjectId, networkObject.name, size); - } + ObjectInfo = networkObject.GetMessageSceneObject(clientId) + }; + var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientId); + NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject.NetworkObjectId, networkObject.name, size); } } @@ -735,18 +715,12 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } - var context = messageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.DestroyObject, NetworkDelivery.ReliableSequenced, m_TargetClientIds.ToArray(), NetworkUpdateStage.PostLateUpdate); - if (context != null) + var message = new DestroyObjectMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - nonNullContext.NetworkWriter.WriteUInt64Packed(networkObject.NetworkObjectId); - - var size = bufferSizeCapture.StopMeasureSegment(); - NetworkManager.NetworkMetrics.TrackObjectDestroySent(m_TargetClientIds, networkObject.NetworkObjectId, networkObject.name, size); - } + NetworkObjectId = networkObject.NetworkObjectId + }; + var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, m_TargetClientIds); + NetworkManager.NetworkMetrics.TrackObjectDestroySent(m_TargetClientIds, networkObject.NetworkObjectId, networkObject.name, size); } } } @@ -772,34 +746,7 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } } - - /// - /// This will write all client observable NetworkObjects to the 's stream while also - /// adding the client to each 's list only if - /// observable to the client. - /// Maximum number of objects that could theoretically be serialized is 65536 for now - /// - /// the client identifier used to determine if a spawned NetworkObject is observable - /// contains the writer used for serialization - internal void SerializeObservedNetworkObjects(ulong clientId, ref ConnectionApprovedMessage message) - { - if (SpawnedObjectsList.Count == 0) - { - return; - } - message.SceneObjects = - new NativeArray(SpawnedObjectsList.Count, Allocator.TempJob); - foreach (var sobj in SpawnedObjectsList) - { - if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(clientId)) - { - sobj.Observers.Add(clientId); - message.SceneObjects[message.SceneObjectCount++] = sobj.GetMessageSceneObject(clientId); - } - } - } - /// /// This will write all client observable NetworkObjects to the 's stream while also diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs b/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs index 7d6e3e19ad..521a2c715c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs @@ -12,18 +12,8 @@ public DummyMessageHandler(NetworkManager networkManager) NetworkManager = networkManager; } - public void HandleAddObject(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleAddObject)); - - public void HandleDestroyObject(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleDestroyObject)); - public void HandleSceneEvent(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleSceneEvent)); - - public void HandleChangeOwner(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleChangeOwner)); - - public void HandleTimeSync(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleTimeSync)); - - public void HandleNetworkVariableDelta(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleNetworkVariableDelta)); - + public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType) { VerifyCalled(nameof(MessageReceiveQueueItem)); @@ -44,8 +34,6 @@ public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receive public void HandleNamedMessage(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleNamedMessage)); - public void HandleNetworkLog(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleNetworkLog)); - public void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleAllClientsSwitchSceneCompleted)); private void VerifyCalled(string method) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs index f4ca9d0ac9..79d3c6d752 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -35,18 +35,6 @@ public void MessageHandlerReceivedMessageServerClient() // This has to be done post start networkManager.MessageQueueContainer.EnableBatchedMessages(false); - // Should not cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream2 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.CreateObject, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream2.GetBuffer(), 0, (int)messageStream2.Length), 0); - - // Should not cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream3 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.DestroyObject, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream3.GetBuffer(), 0, (int)messageStream3.Length), 0); - // Should not cause log (client only) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); @@ -54,25 +42,6 @@ public void MessageHandlerReceivedMessageServerClient() using var messageStream4 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.SceneEvent, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); networkManager.HandleIncomingData(0, new ArraySegment(messageStream4.GetBuffer(), 0, (int)messageStream4.Length), 0); - // Should not cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream5 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ChangeOwner, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream5.GetBuffer(), 0, (int)messageStream5.Length), 0); - - // Should not cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream6 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.TimeSync, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream6.GetBuffer(), 0, (int)messageStream6.Length), 0); - - // Should cause log (server and client) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNetworkVariableDelta)); - using var messageStream7 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.NetworkVariableDelta, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream7.GetBuffer(), 0, (int)messageStream7.Length), 0); - // Should cause log (server and client) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); @@ -87,13 +56,6 @@ public void MessageHandlerReceivedMessageServerClient() using var messageStream9 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.NamedMessage, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); networkManager.HandleIncomingData(0, new ArraySegment(messageStream9.GetBuffer(), 0, (int)messageStream9.Length), 0); - // Should cause log (server only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNetworkLog)); - using var messageStream10 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ServerLog, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream10.GetBuffer(), 0, (int)messageStream10.Length), 0); - // Stop server to trigger full shutdown networkManager.Shutdown(); @@ -106,21 +68,6 @@ public void MessageHandlerReceivedMessageServerClient() // Disable batching to make the RPCs come straight through // This has to be done post start (and post restart since the queue container is reset) networkManager.MessageQueueContainer.EnableBatchedMessages(false); - - // Should cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleAddObject)); - using var messageStream13 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.CreateObject, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream13.GetBuffer(), 0, (int)messageStream13.Length), 0); - - // Should cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleDestroyObject)); - using var messageStream14 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.DestroyObject, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream14.GetBuffer(), 0, (int)messageStream14.Length), 0); - // Should cause log (client only) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); @@ -128,27 +75,6 @@ public void MessageHandlerReceivedMessageServerClient() using var messageStream15 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.SceneEvent, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); networkManager.HandleIncomingData(0, new ArraySegment(messageStream15.GetBuffer(), 0, (int)messageStream15.Length), 0); - // Should cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleChangeOwner)); - using var messageStream16 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ChangeOwner, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream16.GetBuffer(), 0, (int)messageStream16.Length), 0); - - // Should cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleTimeSync)); - using var messageStream17 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.TimeSync, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream17.GetBuffer(), 0, (int)messageStream17.Length), 0); - - // Should cause log (server and client) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNetworkVariableDelta)); - using var messageStream18 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.NetworkVariableDelta, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream18.GetBuffer(), 0, (int)messageStream18.Length), 0); - // Should cause log (server and client) // Everything should log MessageReceiveQueueItem even if ignored LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); @@ -163,12 +89,6 @@ public void MessageHandlerReceivedMessageServerClient() using var messageStream20 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.NamedMessage, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); networkManager.HandleIncomingData(0, new ArraySegment(messageStream20.GetBuffer(), 0, (int)messageStream20.Length), 0); - // Should not cause log (server only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - using var messageStream21 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.ServerLog, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream21.GetBuffer(), 0, (int)messageStream21.Length), 0); - // Full cleanup networkManager.Shutdown(); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs index 970e8fe569..f86baeffe7 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs @@ -14,22 +14,6 @@ public void Setup() m_Decorator = new InternalMessageHandlerProfilingDecorator(new DummyMessageHandler(null)); } - [Test] - public void HandleAddObjectCallsUnderlyingHandler() - { - m_Decorator.HandleAddObject(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleAddObject)); - } - - [Test] - public void HandleDestroyObjectCallsUnderlyingHandler() - { - m_Decorator.HandleDestroyObject(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleDestroyObject)); - } - [Test] public void HandleSceneEventCallsUnderlyingHandler() { @@ -38,22 +22,6 @@ public void HandleSceneEventCallsUnderlyingHandler() LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleSceneEvent)); } - [Test] - public void HandleChangeOwnerCallsUnderlyingHandler() - { - m_Decorator.HandleChangeOwner(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleChangeOwner)); - } - - [Test] - public void HandleNetworkVariableDeltaCallsUnderlyingHandler() - { - m_Decorator.HandleNetworkVariableDelta(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleNetworkVariableDelta)); - } - [Test] public void HandleUnnamedMessageCallsUnderlyingHandler() { @@ -70,14 +38,6 @@ public void HandleNamedMessageCallsUnderlyingHandler() LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleNamedMessage)); } - [Test] - public void HandleNetworkLogCallsUnderlyingHandler() - { - m_Decorator.HandleNetworkLog(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleNetworkLog)); - } - [Test] public void MessageReceiveQueueItemCallsUnderlyingHandler() { From 1436cf8d714f3b7068714cac35df265152238b74 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 13 Sep 2021 21:35:24 -0500 Subject: [PATCH 28/58] Finished converting all messages over and removed all the now-dead code. Several tests are failing... fixing them will be the next checkin. --- .../Components/NetworkAnimator.cs | 100 +- .../Components/NetworkTransform.cs | 22 +- .../Editor/CodeGen/CodeGenHelpers.cs | 4 +- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 2657 ++++------------- .../CodeGen/RuntimeAccessModifiersILPP.cs | 20 +- .../Runtime/Configuration/HashSize.cs | 2 +- .../Runtime/Configuration/NetworkConfig.cs | 127 +- .../Runtime/Core/NetworkBehaviour.cs | 320 +- .../Runtime/Core/NetworkManager.cs | 172 +- .../Runtime/Core/NetworkObject.cs | 239 +- .../Runtime/Core/SnapshotSystem.cs | 55 +- .../Runtime/Messaging/CustomMessageManager.cs | 99 +- .../Messaging/IInternalMessageHandler.cs | 13 - .../Messaging/IInternalMessageHandler.cs.meta | 3 - .../Messaging/InternalCommandContext.cs | 45 - .../Messaging/InternalMessageHandler.cs | 79 - .../Runtime/Messaging/MessageBatcher.cs | 208 -- .../MessageQueue/MessageFrameItem.cs | 33 - .../MessageQueue/MessageQueueContainer.cs | 818 ----- .../MessageQueueContainer.cs.meta | 11 - .../MessageQueue/MessageQueueHistoryFrame.cs | 244 -- .../MessageQueueHistoryFrame.cs.meta | 11 - .../MessageQueue/MessageQueueProcessor.cs | 279 -- .../MessageQueueProcessor.cs.meta | 11 - .../Messaging/Messages/NamedMessage.cs | 22 + .../NamedMessage.cs.meta} | 2 +- .../Runtime/Messaging/Messages/RpcMessage.cs | 102 + .../RpcMessage.cs.meta} | 2 +- .../Messaging/Messages/SceneEventMessage.cs | 19 + .../SceneEventMessage.cs.meta} | 2 +- .../Messaging/Messages/UnnamedMessage.cs | 17 + .../UnnamedMessage.cs.meta} | 2 +- .../Runtime/Messaging/RpcParams.cs | 56 +- .../Runtime/Metrics/BufferSizeCapture.cs | 65 - .../Runtime/Metrics/BufferSizeCapture.cs.meta | 11 - .../Collections/NetworkDictionary.cs | 214 -- .../Collections/NetworkList.cs | 241 -- .../NetworkVariable/Collections/NetworkSet.cs | 139 - .../NetworkVariable/NetworkVariable.cs | 40 - .../NetworkVariable/NetworkVariableBase.cs | 25 - ...nternalMessageHandlerProfilingDecorator.cs | 82 - ...alMessageHandlerProfilingDecorator.cs.meta | 11 - .../SceneManagement/NetworkSceneManager.cs | 202 +- .../Runtime/SceneManagement/SceneEventData.cs | 303 +- .../Serialization/AutoNetworkSerializable.cs | 38 - .../AutoNetworkSerializable.cs.meta | 11 - .../Runtime/Serialization/BufferSerializer.cs | 21 +- .../Serialization/BufferSerializerReader.cs | 7 +- .../Serialization/BufferSerializerWriter.cs | 7 +- .../Runtime/Serialization/BytePacker.cs | 10 +- .../Runtime/Serialization/FastBufferReader.cs | 29 + .../FastBufferReaderExtensions.cs | 53 +- .../Runtime/Serialization/FastBufferWriter.cs | 32 +- .../FastBufferWriterExtensions.cs | 119 +- .../IBufferSerializerImplementation.cs | 3 +- .../Serialization/INetworkSerializable.cs | 2 +- .../Runtime/Serialization/NetworkBuffer.cs | 601 ---- .../Serialization/NetworkBuffer.cs.meta | 11 - .../Runtime/Serialization/NetworkReader.cs | 2296 -------------- .../Serialization/NetworkReader.cs.meta | 11 - .../Serialization/NetworkSerializer.cs | 910 ------ .../Serialization/NetworkSerializer.cs.meta | 11 - .../Runtime/Serialization/NetworkWriter.cs | 1875 ------------ .../Serialization/NetworkWriter.cs.meta | 11 - .../Serialization/Pooled/NetworkBufferPool.cs | 98 - .../Pooled/NetworkBufferPool.cs.meta | 11 - .../Serialization/Pooled/NetworkReaderPool.cs | 60 - .../Pooled/NetworkReaderPool.cs.meta | 11 - .../Serialization/Pooled/NetworkWriterPool.cs | 60 - .../Pooled/NetworkWriterPool.cs.meta | 11 - .../Pooled/PooledNetworkBuffer.cs | 44 - .../Pooled/PooledNetworkBuffer.cs.meta | 11 - .../Pooled/PooledNetworkReader.cs | 46 - .../Pooled/PooledNetworkReader.cs.meta | 11 - .../Pooled/PooledNetworkWriter.cs | 46 - .../Pooled/PooledNetworkWriter.cs.meta | 11 - .../Serialization/SerializationManager.cs | 146 - .../SerializationManager.cs.meta | 11 - .../Runtime/Spawning/NetworkSpawnManager.cs | 223 +- .../Tests/Editor/DummyMessageHandler.cs | 44 - .../Tests/Editor/DummyMessageHandler.cs.meta | 11 - .../Tests/Editor/MessageBatcherTests.cs | 92 - .../Tests/Editor/MessagePacker.cs | 25 - .../Tests/Editor/MessagePacker.cs.meta | 11 - .../Tests/Editor/NetworkBufferTests.cs | 913 ------ .../Tests/Editor/NetworkBufferTests.cs.meta | 11 - .../NetworkManagerMessageHandlerTests.cs | 97 - .../NetworkManagerMessageHandlerTests.cs.meta | 12 +- .../Tests/Editor/NetworkSerializerTests.cs | 1535 ---------- .../Editor/NetworkSerializerTests.cs.meta | 11 - .../Tests/Editor/Profiling.meta | 8 - ...alMessageHandlerProfilingDecoratorTests.cs | 49 - ...sageHandlerProfilingDecoratorTests.cs.meta | 11 - .../NetworkUpdateStagesComponent.cs | 191 -- .../NetworkUpdateStagesComponent.cs.meta | 11 - .../Runtime/Messaging/NamedMessageTests.cs | 58 +- .../Runtime/Messaging/UnnamedMessageTests.cs | 62 +- .../NetworkObjectSceneSerializationTests.cs | 230 +- .../Tests/Runtime/NetworkVarBufferCopyTest.cs | 38 +- .../Tests/Runtime/NetworkVariableTests.cs | 26 +- .../Tests/Runtime/RpcPipelineTestComponent.cs | 48 +- .../Tests/Runtime/RpcQueueTests.cs | 98 +- .../Resources/PerformanceTestRunInfo.json | 79 - .../PerformanceTestRunInfo.json.meta | 7 - .../NetworkPrefabHandlerObjectPoolOverride.cs | 6 - .../HybridScripts/RpcQueueManualTests.cs | 6 - .../Tests/Manual/Scripts/RandomMovement.cs | 84 + .../Manual/Scripts/StatsInfoContainer.cs | 8 +- .../Assets/Tests/Runtime/MessageOrdering.cs | 12 +- .../Tests/Runtime/RpcINetworkSerializable.cs | 20 +- .../Assets/Tests/Runtime/RpcTestsAutomated.cs | 24 +- .../Tests/Runtime/Support/SpawnRpcDespawn.cs | 4 +- .../Support/SpawnRpcDespawnInstanceHandler.cs | 4 +- 113 files changed, 1917 insertions(+), 15916 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs rename com.unity.netcode.gameobjects/Runtime/Messaging/{InternalMessageHandler.cs.meta => Messages/NamedMessage.cs.meta} (83%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs rename com.unity.netcode.gameobjects/Runtime/Messaging/{MessageBatcher.cs.meta => Messages/RpcMessage.cs.meta} (83%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs rename com.unity.netcode.gameobjects/Runtime/Messaging/{MessageQueue/MessageFrameItem.cs.meta => Messages/SceneEventMessage.cs.meta} (83%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs rename com.unity.netcode.gameobjects/Runtime/Messaging/{InternalCommandContext.cs.meta => Messages/UnnamedMessage.cs.meta} (83%) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Profiling.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs.meta delete mode 100644 testproject/Assets/Resources/PerformanceTestRunInfo.json delete mode 100644 testproject/Assets/Resources/PerformanceTestRunInfo.json.meta diff --git a/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs index a1f16d86de..c8100d45a5 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs @@ -76,7 +76,7 @@ public bool SetTrigger(int key) return TriggerParameters.Add(key); } - public void NetworkSerialize(NetworkSerializer serializer) + public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation { SerializeIntParameters(serializer); SerializeFloatParameters(serializer); @@ -85,28 +85,28 @@ public void NetworkSerialize(NetworkSerializer serializer) SerializeAnimatorLayerStates(serializer); } - private void SerializeAnimatorLayerStates(NetworkSerializer serializer) + private void SerializeAnimatorLayerStates(BufferSerializer serializer) where T: IBufferSerializerImplementation { - int layerCount = serializer.IsReading ? 0 : LayerStates.Length; - serializer.Serialize(ref layerCount); + int layerCount = serializer.IsReader ? 0 : LayerStates.Length; + serializer.SerializeValue(ref layerCount); - if (serializer.IsReading && LayerStates.Length != layerCount) + if (serializer.IsReader && LayerStates.Length != layerCount) { LayerStates = new LayerState[layerCount]; } for (int paramIndex = 0; paramIndex < layerCount; paramIndex++) { - var stateHash = serializer.IsReading ? 0 : LayerStates[paramIndex].StateHash; - serializer.Serialize(ref stateHash); + var stateHash = serializer.IsReader ? 0 : LayerStates[paramIndex].StateHash; + serializer.SerializeValue(ref stateHash); - var layerWeight = serializer.IsReading ? 0 : LayerStates[paramIndex].LayerWeight; - serializer.Serialize(ref layerWeight); + var layerWeight = serializer.IsReader ? 0 : LayerStates[paramIndex].LayerWeight; + serializer.SerializeValue(ref layerWeight); - var normalizedStateTime = serializer.IsReading ? 0 : LayerStates[paramIndex].NormalizedStateTime; - serializer.Serialize(ref normalizedStateTime); + var normalizedStateTime = serializer.IsReader ? 0 : LayerStates[paramIndex].NormalizedStateTime; + serializer.SerializeValue(ref normalizedStateTime); - if (serializer.IsReading) + if (serializer.IsReader) { LayerStates[paramIndex] = new LayerState() { @@ -118,103 +118,103 @@ private void SerializeAnimatorLayerStates(NetworkSerializer serializer) } } - private void SerializeTriggerParameters(NetworkSerializer serializer) + private void SerializeTriggerParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation { - int paramCount = serializer.IsReading ? 0 : TriggerParameters.Count; - serializer.Serialize(ref paramCount); + int paramCount = serializer.IsReader ? 0 : TriggerParameters.Count; + serializer.SerializeValue(ref paramCount); - var paramArray = serializer.IsReading ? new int[paramCount] : TriggerParameters.ToArray(); + var paramArray = serializer.IsReader ? new int[paramCount] : TriggerParameters.ToArray(); for (int i = 0; i < paramCount; i++) { - var paramId = serializer.IsReading ? 0 : paramArray[i]; - serializer.Serialize(ref paramId); + var paramId = serializer.IsReader ? 0 : paramArray[i]; + serializer.SerializeValue(ref paramId); - if (serializer.IsReading) + if (serializer.IsReader) { paramArray[i] = paramId; } } - if (serializer.IsReading) + if (serializer.IsReader) { TriggerParameters = new HashSet(paramArray); } } - private void SerializeBoolParameters(NetworkSerializer serializer) + private void SerializeBoolParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation { - int paramCount = serializer.IsReading ? 0 : BoolParameters.Count; - serializer.Serialize(ref paramCount); + int paramCount = serializer.IsReader ? 0 : BoolParameters.Count; + serializer.SerializeValue(ref paramCount); - var paramArray = serializer.IsReading ? new KeyValuePair[paramCount] : BoolParameters.ToArray(); + var paramArray = serializer.IsReader ? new KeyValuePair[paramCount] : BoolParameters.ToArray(); for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { - var paramId = serializer.IsReading ? 0 : paramArray[paramIndex].Key; - serializer.Serialize(ref paramId); + var paramId = serializer.IsReader ? 0 : paramArray[paramIndex].Key; + serializer.SerializeValue(ref paramId); - var paramBool = serializer.IsReading ? false : paramArray[paramIndex].Value; - serializer.Serialize(ref paramBool); + var paramBool = serializer.IsReader ? false : paramArray[paramIndex].Value; + serializer.SerializeValue(ref paramBool); - if (serializer.IsReading) + if (serializer.IsReader) { paramArray[paramIndex] = new KeyValuePair(paramId, paramBool); } } - if (serializer.IsReading) + if (serializer.IsReader) { BoolParameters = paramArray.ToDictionary(pair => pair.Key, pair => pair.Value); } } - private void SerializeFloatParameters(NetworkSerializer serializer) + private void SerializeFloatParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation { - int paramCount = serializer.IsReading ? 0 : FloatParameters.Count; - serializer.Serialize(ref paramCount); + int paramCount = serializer.IsReader ? 0 : FloatParameters.Count; + serializer.SerializeValue(ref paramCount); - var paramArray = serializer.IsReading ? new KeyValuePair[paramCount] : FloatParameters.ToArray(); + var paramArray = serializer.IsReader ? new KeyValuePair[paramCount] : FloatParameters.ToArray(); for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { - var paramId = serializer.IsReading ? 0 : paramArray[paramIndex].Key; - serializer.Serialize(ref paramId); + var paramId = serializer.IsReader ? 0 : paramArray[paramIndex].Key; + serializer.SerializeValue(ref paramId); - var paramFloat = serializer.IsReading ? 0 : paramArray[paramIndex].Value; - serializer.Serialize(ref paramFloat); + var paramFloat = serializer.IsReader ? 0 : paramArray[paramIndex].Value; + serializer.SerializeValue(ref paramFloat); - if (serializer.IsReading) + if (serializer.IsReader) { paramArray[paramIndex] = new KeyValuePair(paramId, paramFloat); } } - if (serializer.IsReading) + if (serializer.IsReader) { FloatParameters = paramArray.ToDictionary(pair => pair.Key, pair => pair.Value); } } - private void SerializeIntParameters(NetworkSerializer serializer) + private void SerializeIntParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation { - int paramCount = serializer.IsReading ? 0 : IntParameters.Count; - serializer.Serialize(ref paramCount); + int paramCount = serializer.IsReader ? 0 : IntParameters.Count; + serializer.SerializeValue(ref paramCount); - var paramArray = serializer.IsReading ? new KeyValuePair[paramCount] : IntParameters.ToArray(); + var paramArray = serializer.IsReader ? new KeyValuePair[paramCount] : IntParameters.ToArray(); for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { - var paramId = serializer.IsReading ? 0 : paramArray[paramIndex].Key; - serializer.Serialize(ref paramId); + var paramId = serializer.IsReader ? 0 : paramArray[paramIndex].Key; + serializer.SerializeValue(ref paramId); - var paramInt = serializer.IsReading ? 0 : paramArray[paramIndex].Value; - serializer.Serialize(ref paramInt); + var paramInt = serializer.IsReader ? 0 : paramArray[paramIndex].Value; + serializer.SerializeValue(ref paramInt); - if (serializer.IsReading) + if (serializer.IsReader) { paramArray[paramIndex] = new KeyValuePair(paramId, paramInt); } } - if (serializer.IsReading) + if (serializer.IsReader) { IntParameters = paramArray.ToDictionary(pair => pair.Key, pair => pair.Value); } diff --git a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs index 214091cbb5..aa35fc91eb 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs @@ -82,48 +82,48 @@ public bool HasScaleZ public float RotAngleX, RotAngleY, RotAngleZ; public float ScaleX, ScaleY, ScaleZ; - public void NetworkSerialize(NetworkSerializer serializer) + public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation { // InLocalSpace + HasXXX Bits - serializer.Serialize(ref Bitset); + serializer.SerializeValue(ref Bitset); // Position Values if (HasPositionX) { - serializer.Serialize(ref PositionX); + serializer.SerializeValue(ref PositionX); } if (HasPositionY) { - serializer.Serialize(ref PositionY); + serializer.SerializeValue(ref PositionY); } if (HasPositionZ) { - serializer.Serialize(ref PositionZ); + serializer.SerializeValue(ref PositionZ); } // RotAngle Values if (HasRotAngleX) { - serializer.Serialize(ref RotAngleX); + serializer.SerializeValue(ref RotAngleX); } if (HasRotAngleY) { - serializer.Serialize(ref RotAngleY); + serializer.SerializeValue(ref RotAngleY); } if (HasRotAngleZ) { - serializer.Serialize(ref RotAngleZ); + serializer.SerializeValue(ref RotAngleZ); } // Scale Values if (HasScaleX) { - serializer.Serialize(ref ScaleX); + serializer.SerializeValue(ref ScaleX); } if (HasScaleY) { - serializer.Serialize(ref ScaleY); + serializer.SerializeValue(ref ScaleY); } if (HasScaleZ) { - serializer.Serialize(ref ScaleZ); + serializer.SerializeValue(ref ScaleZ); } } } diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs index 3bf61d2523..d99ead74ec 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs @@ -264,9 +264,9 @@ public static void AddError(this List diagnostics, SequencePo }); } - public static AssemblyDefinition AssemblyDefinitionFor(ICompiledAssembly compiledAssembly) + public static AssemblyDefinition AssemblyDefinitionFor(ICompiledAssembly compiledAssembly, out PostProcessorAssemblyResolver assemblyResolver) { - var assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly); + assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly); var readerParameters = new ReaderParameters { SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData), diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 1231c23dee..0c59d3a367 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -3,9 +3,11 @@ using System.Linq; using System.Collections.Generic; using System.Reflection; +using System.Runtime.CompilerServices; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; +using Unity.Collections; using Unity.CompilationPipeline.Common.Diagnostics; using Unity.CompilationPipeline.Common.ILPostProcessing; using UnityEngine; @@ -15,6 +17,7 @@ namespace Unity.Netcode.Editor.CodeGen { + internal sealed class NetworkBehaviourILPP : ILPPInterface { public override ILPPInterface GetInstance() => this; @@ -30,10 +33,11 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) return null; } + m_Diagnostics.Clear(); // read - var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly); + var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly, out m_AssemblyResolver); if (assemblyDefinition == null) { m_Diagnostics.AddError($"Cannot read assembly definition: {compiledAssembly.Name}"); @@ -44,13 +48,21 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) var mainModule = assemblyDefinition.MainModule; if (mainModule != null) { + m_MainModule = mainModule; if (ImportReferences(mainModule)) { // process `NetworkBehaviour` types - mainModule.GetTypes() - .Where(t => t.IsSubclassOf(CodeGenHelpers.NetworkBehaviour_FullName)) - .ToList() - .ForEach(b => ProcessNetworkBehaviour(b, compiledAssembly.Defines)); + try + { + mainModule.GetTypes() + .Where(t => t.IsSubclassOf(CodeGenHelpers.NetworkBehaviour_FullName)) + .ToList() + .ForEach(b => ProcessNetworkBehaviour(b, compiledAssembly.Defines)); + } + catch (Exception e) + { + m_Diagnostics.AddError((e.ToString() + e.StackTrace.ToString()).Replace("\n", "|").Replace("\r", "|")); + } } else { @@ -78,6 +90,9 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics); } + private ModuleDefinition m_MainModule; + private PostProcessorAssemblyResolver m_AssemblyResolver; + private MethodReference m_Debug_LogError_MethodRef; private TypeReference m_NetworkManager_TypeRef; private MethodReference m_NetworkManager_getLocalClientId_MethodRef; @@ -91,10 +106,8 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) private FieldReference m_NetworkManager_rpc_name_table_FieldRef; private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef; private TypeReference m_NetworkBehaviour_TypeRef; - private MethodReference m_NetworkBehaviour_BeginSendServerRpc_MethodRef; - private MethodReference m_NetworkBehaviour_EndSendServerRpc_MethodRef; - private MethodReference m_NetworkBehaviour_BeginSendClientRpc_MethodRef; - private MethodReference m_NetworkBehaviour_EndSendClientRpc_MethodRef; + private MethodReference m_NetworkBehaviour_SendServerRpc_MethodRef; + private MethodReference m_NetworkBehaviour_SendClientRpc_MethodRef; private FieldReference m_NetworkBehaviour_rpc_exec_stage_FieldRef; private MethodReference m_NetworkBehaviour_getNetworkManager_MethodRef; private MethodReference m_NetworkBehaviour_getOwnerClientId_MethodRef; @@ -106,49 +119,16 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) private FieldReference m_ServerRpcParams_Receive_FieldRef; private FieldReference m_ServerRpcParams_Receive_SenderClientId_FieldRef; private TypeReference m_ClientRpcParams_TypeRef; - private TypeReference m_NetworkSerializer_TypeRef; - private MethodReference m_NetworkSerializer_SerializeBool_MethodRef; - private MethodReference m_NetworkSerializer_SerializeChar_MethodRef; - private MethodReference m_NetworkSerializer_SerializeSbyte_MethodRef; - private MethodReference m_NetworkSerializer_SerializeByte_MethodRef; - private MethodReference m_NetworkSerializer_SerializeShort_MethodRef; - private MethodReference m_NetworkSerializer_SerializeUshort_MethodRef; - private MethodReference m_NetworkSerializer_SerializeInt_MethodRef; - private MethodReference m_NetworkSerializer_SerializeUint_MethodRef; - private MethodReference m_NetworkSerializer_SerializeLong_MethodRef; - private MethodReference m_NetworkSerializer_SerializeUlong_MethodRef; - private MethodReference m_NetworkSerializer_SerializeFloat_MethodRef; - private MethodReference m_NetworkSerializer_SerializeDouble_MethodRef; - private MethodReference m_NetworkSerializer_SerializeString_MethodRef; - private MethodReference m_NetworkSerializer_SerializeColor_MethodRef; - private MethodReference m_NetworkSerializer_SerializeColor32_MethodRef; - private MethodReference m_NetworkSerializer_SerializeVector2_MethodRef; - private MethodReference m_NetworkSerializer_SerializeVector3_MethodRef; - private MethodReference m_NetworkSerializer_SerializeVector4_MethodRef; - private MethodReference m_NetworkSerializer_SerializeQuaternion_MethodRef; - private MethodReference m_NetworkSerializer_SerializeRay_MethodRef; - private MethodReference m_NetworkSerializer_SerializeRay2D_MethodRef; - private MethodReference m_NetworkSerializer_SerializeBoolArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeCharArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeSbyteArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeByteArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeShortArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeUshortArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeIntArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeUintArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeLongArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeUlongArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeFloatArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeDoubleArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeStringArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeColorArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeColor32Array_MethodRef; - private MethodReference m_NetworkSerializer_SerializeVector2Array_MethodRef; - private MethodReference m_NetworkSerializer_SerializeVector3Array_MethodRef; - private MethodReference m_NetworkSerializer_SerializeVector4Array_MethodRef; - private MethodReference m_NetworkSerializer_SerializeQuaternionArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeRayArray_MethodRef; - private MethodReference m_NetworkSerializer_SerializeRay2DArray_MethodRef; + + private TypeReference m_FastBufferWriter_TypeRef; + private MethodReference m_FastBufferWriter_Constructor; + private MethodReference m_FastBufferWriter_Dispose; + private Dictionary m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary(); + private List m_FastBufferWriter_ExtensionMethodRefs = new List(); + + private TypeReference m_FastBufferReader_TypeRef; + private Dictionary m_FastBufferReader_ReadValue_MethodRefs = new Dictionary(); + private List m_FastBufferReader_ExtensionMethodRefs = new List(); private const string k_Debug_LogError = nameof(Debug.LogError); private const string k_NetworkManager_LocalClientId = nameof(NetworkManager.LocalClientId); @@ -160,11 +140,9 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) private const string k_NetworkManager_rpc_func_table = nameof(NetworkManager.__rpc_func_table); private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table); - private const string k_NetworkBehaviour_BeginSendServerRpc = nameof(NetworkBehaviour.__beginSendServerRpc); - private const string k_NetworkBehaviour_EndSendServerRpc = nameof(NetworkBehaviour.__endSendServerRpc); - private const string k_NetworkBehaviour_BeginSendClientRpc = nameof(NetworkBehaviour.__beginSendClientRpc); - private const string k_NetworkBehaviour_EndSendClientRpc = nameof(NetworkBehaviour.__endSendClientRpc); private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage); + private const string k_NetworkBehaviour_SendServerRpc = nameof(NetworkBehaviour.SendServerRpc); + private const string k_NetworkBehaviour_SendClientRpc = nameof(NetworkBehaviour.SendClientRpc); private const string k_NetworkBehaviour_NetworkManager = nameof(NetworkBehaviour.NetworkManager); private const string k_NetworkBehaviour_OwnerClientId = nameof(NetworkBehaviour.OwnerClientId); @@ -253,17 +231,11 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) { switch (methodInfo.Name) { - case k_NetworkBehaviour_BeginSendServerRpc: - m_NetworkBehaviour_BeginSendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo); - break; - case k_NetworkBehaviour_EndSendServerRpc: - m_NetworkBehaviour_EndSendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo); - break; - case k_NetworkBehaviour_BeginSendClientRpc: - m_NetworkBehaviour_BeginSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo); + case k_NetworkBehaviour_SendServerRpc: + m_NetworkBehaviour_SendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo); break; - case k_NetworkBehaviour_EndSendClientRpc: - m_NetworkBehaviour_EndSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo); + case k_NetworkBehaviour_SendClientRpc: + m_NetworkBehaviour_SendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo); break; } } @@ -278,7 +250,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) } } - var networkHandlerDelegateType = typeof(Action); + var networkHandlerDelegateType = typeof(NetworkManager.RpcReceive); m_NetworkHandlerDelegateCtor_MethodRef = moduleDefinition.ImportReference(networkHandlerDelegateType.GetConstructor(new[] { typeof(object), typeof(IntPtr) })); var rpcParamsType = typeof(__RpcParams); @@ -321,199 +293,77 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) var clientRpcParamsType = typeof(ClientRpcParams); m_ClientRpcParams_TypeRef = moduleDefinition.ImportReference(clientRpcParamsType); - var networkSerializerType = typeof(NetworkSerializer); - m_NetworkSerializer_TypeRef = moduleDefinition.ImportReference(networkSerializerType); - foreach (var methodInfo in networkSerializerType.GetMethods()) + var fastBufferWriterType = typeof(FastBufferWriter); + m_FastBufferWriter_TypeRef = moduleDefinition.ImportReference(fastBufferWriterType); + + m_FastBufferWriter_Constructor = moduleDefinition.ImportReference( + fastBufferWriterType.GetConstructor(new[] {typeof(int), typeof(Allocator), typeof(int)})); + m_FastBufferWriter_Dispose = moduleDefinition.ImportReference(fastBufferWriterType.GetMethod("Dispose")); + + var FstBufferReaderType = typeof(FastBufferReader); + m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(FstBufferReaderType); + + // Find all extension methods for FastBufferReader and FastBufferWriter to enable user-implemented + // methods to be called. + List assemblies = new List(); + assemblies.Add(m_MainModule.Assembly); + foreach (var reference in m_MainModule.AssemblyReferences) { - if (methodInfo.Name != nameof(NetworkSerializer.Serialize)) - { - continue; - } + assemblies.Add(m_AssemblyResolver.Resolve(reference)); + } - var methodParams = methodInfo.GetParameters(); - if (methodParams.Length != 1) + var extensionConstructor = + moduleDefinition.ImportReference(typeof(ExtensionAttribute).GetConstructor(new Type[] { })); + foreach(var assembly in assemblies) + { + foreach (var module in assembly.Modules) { - continue; - } + foreach (var type in module.Types) + { + var resolvedType = type.Resolve(); + if (!resolvedType.IsSealed || !resolvedType.IsAbstract || resolvedType.IsNested) + { + continue; + } + foreach (var method in type.Methods) + { + if (!method.IsStatic) + { + continue; + } - var paramType = methodParams[0].ParameterType; - if (paramType.IsByRef == false) - { - continue; - } + var isExtension = false; + + foreach (var attr in method.CustomAttributes) + { + if (attr.Constructor.Resolve() == extensionConstructor.Resolve()) + { + isExtension = true; + } + } - var paramTypeName = paramType.Name; + if (!isExtension) + { + continue; + } - if (paramTypeName == typeof(bool).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeBool_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(char).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeChar_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(sbyte).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeSbyte_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(byte).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeByte_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(short).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeShort_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(ushort).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeUshort_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(int).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeInt_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(uint).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeUint_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(long).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeLong_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(ulong).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeUlong_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(float).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeFloat_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(double).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeDouble_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(string).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeString_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Color).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeColor_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Color32).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeColor32_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Vector2).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeVector2_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Vector3).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeVector3_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Vector4).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeVector4_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Quaternion).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeQuaternion_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Ray).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeRay_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Ray2D).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeRay2D_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(bool[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeBoolArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(char[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeCharArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(sbyte[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeSbyteArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(byte[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeByteArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(short[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeShortArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(ushort[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeUshortArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(int[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeIntArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(uint[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeUintArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(long[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeLongArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(ulong[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeUlongArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(float[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeFloatArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(double[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeDoubleArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(string[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeStringArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Color[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeColorArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Color32[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeColor32Array_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Vector2[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeVector2Array_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Vector3[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeVector3Array_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Vector4[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeVector4Array_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Quaternion[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeQuaternionArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Ray[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeRayArray_MethodRef = moduleDefinition.ImportReference(methodInfo); - } - else if (paramTypeName == typeof(Ray2D[]).MakeByRefType().Name) - { - m_NetworkSerializer_SerializeRay2DArray_MethodRef = moduleDefinition.ImportReference(methodInfo); + var parameters = method.Parameters; + + if (parameters.Count == 2 + && parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve()) + { + m_FastBufferWriter_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method)); + } + else if (parameters.Count == 2 + && parameters[0].ParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve()) + { + m_FastBufferReader_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method)); + } + } + } } } - + return true; } @@ -613,6 +463,39 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass typeDefinition.Methods.Add(newGetTypeNameMethod); } + + // Weird behavior from Cecil: When importing a reference to a specific implementation of a generic + // method, it's importing the main module as a reference into itself. This causes Unity to have issues + // when attempting to iterate the assemblies to discover unit tests, as it goes into infinite recursion + // and eventually hits a stack overflow. I wasn't able to find any way to stop Cecil from importing the module + // into itself, so at the end of it all, we're just going to go back and remove it again. + var moduleName = m_MainModule.Name; + if (moduleName.EndsWith(".dll") || moduleName.EndsWith(".exe")) + { + moduleName = moduleName.Substring(0, moduleName.Length-4); + } + + foreach (var reference in m_MainModule.AssemblyReferences) + { + var referenceName = reference.Name.Split(',')[0]; + if (referenceName.EndsWith(".dll") || referenceName.EndsWith(".exe")) + { + referenceName = referenceName.Substring(0, referenceName.Length-4); + } + + if (moduleName == referenceName) + { + try + { + m_MainModule.AssemblyReferences.Remove(reference); + break; + } + catch (Exception e) + { + // + } + } + } } private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinition) @@ -681,34 +564,219 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio return null; } + // Checks for IsSerializable are moved to later as the check is now done by dynamically seeing if any valid + // serializer OR extension method exists for it. + return rpcAttribute; + } + + private MethodInfo GetWriteMethodViaSystemReflection(Type objectType, string name, Type paramType) + { + foreach (var method in objectType.GetMethods()) + { + if (method.Name == name) + { + var parameters = method.GetParameters(); - int paramCount = methodDefinition.Parameters.Count; - for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex) + if (parameters.Length == 0 || (parameters.Length > 1 && !parameters[1].IsOptional)) + { + continue; + } + + var checkType = paramType; + if (paramType.IsArray) + { + checkType = paramType.GetElementType(); + } + + if ((parameters[0].ParameterType == checkType) || (parameters[0].ParameterType == checkType.MakeByRefType() && parameters[0].IsIn) && parameters[0].ParameterType.IsArray == paramType.IsArray) + { + return method; + } + if (method.IsGenericMethod) + { + var genericParamType = parameters[0].ParameterType; + if (genericParamType.IsByRef) + { + genericParamType = genericParamType.GetElementType(); + } + + if (genericParamType.IsArray == paramType.IsArray) + { + try + { + return method.MakeGenericMethod(checkType); + } + catch (Exception e) + { + } + } + } + } + } + + return null; + } + + private bool GetWriteMethodForParameter(TypeReference paramType, out MethodReference methodRef) + { + var assemblyQualifiedName = paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName; + var foundMethodRef = m_FastBufferWriter_WriteValue_MethodRefs.TryGetValue(assemblyQualifiedName, out methodRef); + + if (!foundMethodRef) { - var paramDef = methodDefinition.Parameters[paramIndex]; - var paramType = paramDef.ParameterType; + foreach (var method in m_FastBufferWriter_ExtensionMethodRefs) + { + var parameters = method.Resolve().Parameters; + + if (method.Name == "WriteValueSafe") + { + if (parameters[1].IsIn) + { + if (parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() + && ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray) + { + methodRef = method; + m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; + return true; + } + } + else + { - // Serializable - if (paramType.IsSerializable()) + if (parameters[1].ParameterType.Resolve() == paramType.Resolve() + && parameters[1].ParameterType.IsArray == paramType.IsArray) + { + methodRef = method; + m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; + return true; + } + } + } + } + + var systemType = Type.GetType(paramType.FullName); + if (systemType == null) { - continue; + systemType = Type.GetType(assemblyQualifiedName); + if (systemType == null) + { + throw new Exception("Couldn't find type for " + paramType.FullName + ", " + + paramType.Resolve().Module.Assembly.FullName); + } } - // ServerRpcParams - if (paramType.FullName == CodeGenHelpers.ServerRpcParams_FullName && isServerRpc && paramIndex == paramCount - 1) + // Try NetworkSerializable first because INetworkSerializable may also be valid for WriteValueSafe + // and that would cause boxing if so. + var systemMethod = GetWriteMethodViaSystemReflection(typeof(FastBufferWriter), "WriteNetworkSerializable", systemType); + if (systemMethod == null) { - continue; + systemMethod = GetWriteMethodViaSystemReflection(typeof(FastBufferWriter), "WriteValueSafe", systemType); } - // ClientRpcParams - if (paramType.FullName == CodeGenHelpers.ClientRpcParams_FullName && !isServerRpc && paramIndex == paramCount - 1) + if (systemMethod != null) { - continue; + methodRef = m_MainModule.ImportReference(systemMethod); + m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; + foundMethodRef = true; + } + } + + return foundMethodRef; + } + + private static MethodInfo GetReadMethodViaSystemReflection(Type objectType, string name, Type paramType) + { + foreach (var method in objectType.GetMethods()) + { + if (method.Name == name) + { + var parameters = method.GetParameters(); + if (parameters.Length == 0 || (parameters.Length > 1 && !parameters[1].IsOptional)) + { + continue; + } + + var checkType = paramType; + if (paramType.IsArray) + { + checkType = paramType.GetElementType(); + } + + if ((parameters[0].ParameterType == checkType.MakeByRefType() && parameters[0].IsOut) && parameters[0].ParameterType.IsArray == paramType.IsArray) + { + return method; + } + if (method.IsGenericMethod) + { + var genericParamType = parameters[0].ParameterType; + if (genericParamType.IsByRef) + { + genericParamType = genericParamType.GetElementType(); + } + if (genericParamType.IsArray == paramType.IsArray) + { + try + { + return method.MakeGenericMethod(checkType); + } + catch (Exception e) + { + + } + } + } } + } + + return null; + } - m_Diagnostics.AddError(methodDefinition, $"RPC method parameter does not support serialization: {paramType.FullName}"); - rpcAttribute = null; + private bool GetReadMethodForParameter(TypeReference paramType, out MethodReference methodRef) + { + var assemblyQualifiedName = paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName; + + var foundMethodRef = m_FastBufferReader_ReadValue_MethodRefs.TryGetValue(assemblyQualifiedName, out methodRef); + if (!foundMethodRef) + { + foreach (var method in m_FastBufferReader_ExtensionMethodRefs) + { + var parameters = method.Resolve().Parameters; + if ( + method.Name == "ReadValueSafe" + && parameters[1].IsOut + && parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() + && ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray) + { + methodRef = method; + m_FastBufferReader_ReadValue_MethodRefs[assemblyQualifiedName] = methodRef; + return true; + } + } + + var systemType = Type.GetType(paramType.FullName); + if (systemType == null) + { + systemType = Type.GetType(assemblyQualifiedName); + if (systemType == null) + { + throw new Exception("Couldn't find type for " + paramType.FullName + ", " + + paramType.Resolve().Module.Assembly.FullName); + } + } + // Try NetworkSerializable first because INetworkSerializable may also be valid for ReadValueSafe + // and that would cause boxing if so. + var systemMethod = GetReadMethodViaSystemReflection(typeof(FastBufferReader), "ReadNetworkSerializable", systemType); + if (systemMethod == null) + { + systemMethod = GetReadMethodViaSystemReflection(typeof(FastBufferReader), "ReadValueSafe", systemType); + } + if (systemMethod != null) + { + methodRef = m_MainModule.ImportReference(systemMethod); + m_FastBufferReader_ReadValue_MethodRefs[assemblyQualifiedName] = methodRef; + foundMethodRef = true; + } } - return rpcAttribute; + return foundMethodRef; } private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomAttribute rpcAttribute, uint rpcMethodId) @@ -743,8 +811,9 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA methodDefinition.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef)); int netManLocIdx = methodDefinition.Body.Variables.Count - 1; // NetworkSerializer serializer; - methodDefinition.Body.Variables.Add(new VariableDefinition(m_NetworkSerializer_TypeRef)); + methodDefinition.Body.Variables.Add(new VariableDefinition(m_FastBufferWriter_TypeRef)); int serializerLocIdx = methodDefinition.Body.Variables.Count - 1; + // XXXRpcParams if (!hasRpcParams) { @@ -813,7 +882,8 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA instructions.Add(processor.Create(OpCodes.Ldarg_0)); instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_getOwnerClientId_MethodRef)); instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkManager_getLocalClientId_MethodRef)); + instructions.Add( + processor.Create(OpCodes.Callvirt, m_NetworkManager_getLocalClientId_MethodRef)); instructions.Add(processor.Create(OpCodes.Ceq)); instructions.Add(processor.Create(OpCodes.Ldc_I4, 0)); instructions.Add(processor.Create(OpCodes.Ceq)); @@ -824,14 +894,15 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA // if (LogLevel.Normal > networkManager.LogLevel) instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); instructions.Add(processor.Create(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)LogLevel.Normal)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, (int) LogLevel.Normal)); instructions.Add(processor.Create(OpCodes.Cgt)); instructions.Add(processor.Create(OpCodes.Ldc_I4, 0)); instructions.Add(processor.Create(OpCodes.Ceq)); instructions.Add(processor.Create(OpCodes.Brfalse, logNextInstr)); // Debug.LogError(...); - instructions.Add(processor.Create(OpCodes.Ldstr, "Only the owner can invoke a ServerRpc that requires ownership!")); + instructions.Add(processor.Create(OpCodes.Ldstr, + "Only the owner can invoke a ServerRpc that requires ownership!")); instructions.Add(processor.Create(OpCodes.Call, m_Debug_LogError_MethodRef)); instructions.Add(logNextInstr); @@ -839,1786 +910,330 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA instructions.Add(roReturnInstr); instructions.Add(roLastInstr); } + } - // var serializer = BeginSendServerRpc(rpcMethodId, serverRpcParams, rpcDelivery); - instructions.Add(processor.Create(OpCodes.Ldarg_0)); + // var writer = new FastBufferWriter(1300, Allocator.Temp, 1300); + instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300)); + instructions.Add(processor.Create(OpCodes.Ldc_I4_2)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300)); + instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Constructor)); - // rpcMethodId - instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); + var firstInstruction = processor.Create(OpCodes.Nop); + instructions.Add(firstInstruction); - // rpcParams - instructions.Add(hasRpcParams ? processor.Create(OpCodes.Ldarg, paramCount) : processor.Create(OpCodes.Ldloc, rpcParamsIdx)); + // write method parameters into stream + for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex) + { + var paramDef = methodDefinition.Parameters[paramIndex]; + var paramType = paramDef.ParameterType; + // ServerRpcParams + if (paramType.FullName == CodeGenHelpers.ServerRpcParams_FullName && isServerRpc && paramIndex == paramCount - 1) + { + continue; + } + // ClientRpcParams + if (paramType.FullName == CodeGenHelpers.ClientRpcParams_FullName && !isServerRpc && paramIndex == paramCount - 1) + { + continue; + } - // rpcDelivery - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery)); + Instruction jumpInstruction = null; - // BeginSendServerRpc - instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_BeginSendServerRpc_MethodRef)); - instructions.Add(processor.Create(OpCodes.Stloc, serializerLocIdx)); - } - else - { - // ClientRpc - // var serializer = BeginSendClientRpc(rpcMethodId, clientRpcParams, rpcDelivery); - instructions.Add(processor.Create(OpCodes.Ldarg_0)); - - // rpcMethodId - instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); - - // rpcParams - instructions.Add(hasRpcParams ? processor.Create(OpCodes.Ldarg, paramCount) : processor.Create(OpCodes.Ldloc, rpcParamsIdx)); - - // rpcDelivery - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery)); - - // BeginSendClientRpc - instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_BeginSendClientRpc_MethodRef)); - instructions.Add(processor.Create(OpCodes.Stloc, serializerLocIdx)); - } - - // if (serializer != null) - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Brfalse, endInstr)); - - // write method parameters into stream - for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex) - { - var paramDef = methodDefinition.Parameters[paramIndex]; - var paramType = paramDef.ParameterType; - - // C# primitives (+arrays) - - if (paramType == typeSystem.Boolean) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeBool_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Boolean) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeBoolArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Char) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeChar_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Char) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeCharArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.SByte) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyte_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.SByte) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyteArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Byte) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeByte_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Byte) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeByteArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Int16) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeShort_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Int16) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeShortArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.UInt16) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshort_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.UInt16) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshortArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Int32) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Int32) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeIntArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.UInt32) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUint_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.UInt32) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUintArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Int64) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeLong_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Int64) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeLongArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.UInt64) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlong_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.UInt64) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlongArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Single) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeFloat_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Single) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeFloatArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.Double) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeDouble_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Double) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeDoubleArray_MethodRef)); - continue; - } - - if (paramType == typeSystem.String) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeString_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.String) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeStringArray_MethodRef)); - continue; - } - - // Unity primitives (+arrays) - - if (paramType.FullName == CodeGenHelpers.UnityColor_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeColor_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityColor_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeColorArray_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityColor32_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeColor32_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityColor32_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeColor32Array_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityVector2_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector2_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityVector2_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector2Array_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityVector3_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector3_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityVector3_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector3Array_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityVector4_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector4_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityVector4_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector4Array_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityQuaternion_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeQuaternion_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityQuaternion_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeQuaternionArray_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityRay_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeRay_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityRay_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeRayArray_MethodRef)); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityRay2D_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeRay2D_MethodRef)); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityRay2D_FullName) - { - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeRay2DArray_MethodRef)); - continue; - } - - // Enum - - { - var paramEnumIntType = paramType.GetEnumAsInt(); - if (paramEnumIntType != null) - { - if (paramEnumIntType == typeSystem.Int32) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.UInt32) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.UInt32)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUint_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.Byte) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Byte)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeByte_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.SByte) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.SByte)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyte_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.Int16) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int16)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeShort_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.UInt16) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.UInt16)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshort_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.Int64) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int64)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeLong_MethodRef)); - continue; - } - - if (paramEnumIntType == typeSystem.UInt64) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.UInt64)); - int localIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Stloc, localIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, localIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlong_MethodRef)); - continue; - } - } - } - - // Enum array - - if (paramType.IsArray) - { - var paramElemEnumIntType = paramType.GetElementType().GetEnumAsInt(); - if (paramElemEnumIntType != null) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int arrLenLocalIndex = methodDefinition.Body.Variables.Count - 1; - - var endifInstr = processor.Create(OpCodes.Nop); - var arrLenInstr = processor.Create(OpCodes.Nop); - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Brtrue, arrLenInstr)); - instructions.Add(processor.Create(OpCodes.Ldc_I4_M1)); - instructions.Add(processor.Create(OpCodes.Br, endifInstr)); - instructions.Add(arrLenInstr); - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldlen)); - instructions.Add(processor.Create(OpCodes.Conv_I4)); - instructions.Add(endifInstr); - instructions.Add(processor.Create(OpCodes.Stloc, arrLenLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, arrLenLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef)); - - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int counterLocalIndex = methodDefinition.Body.Variables.Count - 1; - - var forBodyInstr = processor.Create(OpCodes.Nop); - var forCheckInstr = processor.Create(OpCodes.Nop); - - instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); - instructions.Add(processor.Create(OpCodes.Stloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Br, forCheckInstr)); - instructions.Add(forBodyInstr); - - if (paramElemEnumIntType == typeSystem.Int32) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_I4)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.UInt32) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.UInt32)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_U4)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUint_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.Byte) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Byte)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_U1)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeByte_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.SByte) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.SByte)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_I1)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyte_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.Int16) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int16)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_I2)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeShort_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.UInt16) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.UInt16)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_U2)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshort_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.Int64) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int64)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_I8)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeLong_MethodRef)); - } - else if (paramElemEnumIntType == typeSystem.UInt64) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.UInt64)); - int enumValLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_I8)); - instructions.Add(processor.Create(OpCodes.Stloc, enumValLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, enumValLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlong_MethodRef)); - } - - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldc_I4_1)); - instructions.Add(processor.Create(OpCodes.Add)); - instructions.Add(processor.Create(OpCodes.Stloc, counterLocalIndex)); - instructions.Add(forCheckInstr); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldloc, arrLenLocalIndex)); - instructions.Add(processor.Create(OpCodes.Clt)); - instructions.Add(processor.Create(OpCodes.Brtrue, forBodyInstr)); - - continue; - } - } - - // INetworkSerializable - - if (paramType.HasInterface(CodeGenHelpers.INetworkSerializable_FullName)) - { - var paramTypeDef = paramType.Resolve(); - var paramTypeNetworkSerialize_MethodDef = paramTypeDef.Methods.FirstOrDefault(m => m.Name == CodeGenHelpers.INetworkSerializable_NetworkSerialize_Name); - var paramTypeNetworkSerialize_MethodRef = methodDefinition.Module.ImportReference(paramTypeNetworkSerialize_MethodDef); - if (paramTypeNetworkSerialize_MethodRef != null) - { - if (paramType.IsValueType) - { - // struct (pass by value) - instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Call, paramTypeNetworkSerialize_MethodRef)); - } - else - { - // class (pass by reference) - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); - int isSetLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldnull)); - instructions.Add(processor.Create(OpCodes.Cgt_Un)); - instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeBool_MethodRef)); - - var notSetInstr = processor.Create(OpCodes.Nop); - - instructions.Add(processor.Create(OpCodes.Ldloc, isSetLocalIndex)); - instructions.Add(processor.Create(OpCodes.Brfalse, notSetInstr)); - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Callvirt, paramTypeNetworkSerialize_MethodRef)); - - instructions.Add(notSetInstr); - } - - continue; - } - } - - // INetworkSerializable[] - if (paramType.IsArray && paramType.GetElementType().HasInterface(CodeGenHelpers.INetworkSerializable_FullName)) - { - var paramElemType = paramType.GetElementType(); - var paramElemTypeDef = paramElemType.Resolve(); - var paramElemNetworkSerialize_MethodDef = paramElemTypeDef.Methods.FirstOrDefault(m => m.Name == CodeGenHelpers.INetworkSerializable_NetworkSerialize_Name); - var paramElemNetworkSerialize_MethodRef = methodDefinition.Module.ImportReference(paramElemNetworkSerialize_MethodDef); - if (paramElemNetworkSerialize_MethodRef != null) - { - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int arrLenLocalIndex = methodDefinition.Body.Variables.Count - 1; - - var endifInstr = processor.Create(OpCodes.Nop); - var arrLenInstr = processor.Create(OpCodes.Nop); - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Brtrue, arrLenInstr)); - instructions.Add(processor.Create(OpCodes.Ldc_I4_M1)); - instructions.Add(processor.Create(OpCodes.Br, endifInstr)); - instructions.Add(arrLenInstr); - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldlen)); - instructions.Add(processor.Create(OpCodes.Conv_I4)); - instructions.Add(endifInstr); - instructions.Add(processor.Create(OpCodes.Stloc, arrLenLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, arrLenLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef)); - - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int counterLocalIndex = methodDefinition.Body.Variables.Count - 1; - - var forBodyInstr = processor.Create(OpCodes.Nop); - var forCheckInstr = processor.Create(OpCodes.Nop); - - instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); - instructions.Add(processor.Create(OpCodes.Stloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Br, forCheckInstr)); - instructions.Add(forBodyInstr); - - if (paramElemType.IsValueType) - { - // struct (pass by value) - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelema, paramElemType)); - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Call, paramElemNetworkSerialize_MethodRef)); - } - else - { - // class (pass by reference) - methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); - int isSetLocalIndex = methodDefinition.Body.Variables.Count - 1; - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_Ref)); - instructions.Add(processor.Create(OpCodes.Ldnull)); - instructions.Add(processor.Create(OpCodes.Cgt_Un)); - instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex)); - - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkSerializer_SerializeBool_MethodRef)); - - var notSetInstr = processor.Create(OpCodes.Nop); - - instructions.Add(processor.Create(OpCodes.Ldloc, isSetLocalIndex)); - instructions.Add(processor.Create(OpCodes.Brfalse, notSetInstr)); - - instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldelem_Ref)); - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Callvirt, paramElemNetworkSerialize_MethodRef)); - - instructions.Add(notSetInstr); - } - - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldc_I4_1)); - instructions.Add(processor.Create(OpCodes.Add)); - instructions.Add(processor.Create(OpCodes.Stloc, counterLocalIndex)); - instructions.Add(forCheckInstr); - instructions.Add(processor.Create(OpCodes.Ldloc, counterLocalIndex)); - instructions.Add(processor.Create(OpCodes.Ldloc, arrLenLocalIndex)); - instructions.Add(processor.Create(OpCodes.Clt)); - instructions.Add(processor.Create(OpCodes.Brtrue, forBodyInstr)); - - continue; - } - } - } - - instructions.Add(endInstr); - - // EndSendServerRpc(serializer, rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc - // EndSendClientRpc(serializer, rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc - if (isServerRpc) - { - // ServerRpc - // EndSendServerRpc(serializer, rpcMethodId, serverRpcParams, rpcDelivery); - instructions.Add(processor.Create(OpCodes.Ldarg_0)); - - // serializer - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - - // rpcMethodId - instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); - - if (hasRpcParams) - { - // rpcParams - instructions.Add(processor.Create(OpCodes.Ldarg, paramCount)); - } - else - { - // default - instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx)); - } - - // rpcDelivery - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery)); - - // EndSendServerRpc - instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_EndSendServerRpc_MethodRef)); - } - else - { - // ClientRpc - // EndSendClientRpc(serializer, rpcMethodId, clientRpcParams, rpcDelivery); - instructions.Add(processor.Create(OpCodes.Ldarg_0)); - - // serializer - instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx)); - - // rpcMethodId - instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); - - if (hasRpcParams) - { - // rpcParams - instructions.Add(processor.Create(OpCodes.Ldarg, paramCount)); - } - else - { - // default - instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx)); - } - - // rpcDelivery - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery)); - - // EndSendClientRpc - instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_EndSendClientRpc_MethodRef)); - } - - instructions.Add(lastInstr); - } - - { - var returnInstr = processor.Create(OpCodes.Ret); - var lastInstr = processor.Create(OpCodes.Nop); - - // if (__rpc_exec_stage == __RpcExecStage.Server) -> ServerRpc - // if (__rpc_exec_stage == __RpcExecStage.Client) -> ClientRpc - instructions.Add(processor.Create(OpCodes.Ldarg_0)); - instructions.Add(processor.Create(OpCodes.Ldfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)(isServerRpc ? NetworkBehaviour.__RpcExecStage.Server : NetworkBehaviour.__RpcExecStage.Client))); - instructions.Add(processor.Create(OpCodes.Ceq)); - instructions.Add(processor.Create(OpCodes.Brfalse, returnInstr)); - - // if (networkManager.IsServer || networkManager.IsHost) -> ServerRpc - // if (networkManager.IsClient || networkManager.IsHost) -> ClientRpc - instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); - instructions.Add(processor.Create(OpCodes.Callvirt, isServerRpc ? m_NetworkManager_getIsServer_MethodRef : m_NetworkManager_getIsClient_MethodRef)); - instructions.Add(processor.Create(OpCodes.Brtrue, lastInstr)); - instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); - instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkManager_getIsHost_MethodRef)); - instructions.Add(processor.Create(OpCodes.Brtrue, lastInstr)); - - instructions.Add(returnInstr); - instructions.Add(lastInstr); - } - - instructions.Reverse(); - instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction)); - } - - private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute) - { - var typeSystem = methodDefinition.Module.TypeSystem; - var nhandler = new MethodDefinition( - $"{methodDefinition.Name}__nhandler", - MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, - methodDefinition.Module.TypeSystem.Void); - nhandler.Parameters.Add(new ParameterDefinition("target", ParameterAttributes.None, m_NetworkBehaviour_TypeRef)); - nhandler.Parameters.Add(new ParameterDefinition("serializer", ParameterAttributes.None, m_NetworkSerializer_TypeRef)); - nhandler.Parameters.Add(new ParameterDefinition("rpcParams", ParameterAttributes.None, m_RpcParams_TypeRef)); - - var processor = nhandler.Body.GetILProcessor(); - var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName; - var requireOwnership = true; // default value MUST be = `ServerRpcAttribute.RequireOwnership` - foreach (var attrField in rpcAttribute.Fields) - { - switch (attrField.Name) - { - case k_ServerRpcAttribute_RequireOwnership: - requireOwnership = attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value; - break; - } - } - - nhandler.Body.InitLocals = true; - // NetworkManager networkManager; - nhandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef)); - int netManLocIdx = nhandler.Body.Variables.Count - 1; - - { - var returnInstr = processor.Create(OpCodes.Ret); - var lastInstr = processor.Create(OpCodes.Nop); - - // networkManager = this.NetworkManager; - processor.Emit(OpCodes.Ldarg_0); - processor.Emit(OpCodes.Call, m_NetworkBehaviour_getNetworkManager_MethodRef); - processor.Emit(OpCodes.Stloc, netManLocIdx); - - // if (networkManager == null || !networkManager.IsListening) return; - processor.Emit(OpCodes.Ldloc, netManLocIdx); - processor.Emit(OpCodes.Brfalse, returnInstr); - processor.Emit(OpCodes.Ldloc, netManLocIdx); - processor.Emit(OpCodes.Callvirt, m_NetworkManager_getIsListening_MethodRef); - processor.Emit(OpCodes.Brtrue, lastInstr); - - processor.Append(returnInstr); - processor.Append(lastInstr); - } - - if (isServerRpc && requireOwnership) - { - var roReturnInstr = processor.Create(OpCodes.Ret); - var roLastInstr = processor.Create(OpCodes.Nop); - - // if (rpcParams.Server.Receive.SenderClientId != target.OwnerClientId) { ... } return; - processor.Emit(OpCodes.Ldarg_2); - processor.Emit(OpCodes.Ldfld, m_RpcParams_Server_FieldRef); - processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_FieldRef); - processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_SenderClientId_FieldRef); - processor.Emit(OpCodes.Ldarg_0); - processor.Emit(OpCodes.Call, m_NetworkBehaviour_getOwnerClientId_MethodRef); - processor.Emit(OpCodes.Ceq); - processor.Emit(OpCodes.Ldc_I4, 0); - processor.Emit(OpCodes.Ceq); - processor.Emit(OpCodes.Brfalse, roLastInstr); - - var logNextInstr = processor.Create(OpCodes.Nop); - - // if (LogLevel.Normal > networkManager.LogLevel) - processor.Emit(OpCodes.Ldloc, netManLocIdx); - processor.Emit(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef); - processor.Emit(OpCodes.Ldc_I4, (int)LogLevel.Normal); - processor.Emit(OpCodes.Cgt); - processor.Emit(OpCodes.Ldc_I4, 0); - processor.Emit(OpCodes.Ceq); - processor.Emit(OpCodes.Brfalse, logNextInstr); - - // Debug.LogError(...); - processor.Emit(OpCodes.Ldstr, "Only the owner can invoke a ServerRpc that requires ownership!"); - processor.Emit(OpCodes.Call, m_Debug_LogError_MethodRef); - - processor.Append(logNextInstr); - - processor.Append(roReturnInstr); - processor.Append(roLastInstr); - } - - // read method parameters from stream - int paramCount = methodDefinition.Parameters.Count; - int[] paramLocalMap = new int[paramCount]; - for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex) - { - var paramDef = methodDefinition.Parameters[paramIndex]; - var paramType = paramDef.ParameterType; - - // local variable - nhandler.Body.Variables.Add(new VariableDefinition(paramType)); - int localIndex = nhandler.Body.Variables.Count - 1; - paramLocalMap[paramIndex] = localIndex; - - // C# primitives (+arrays) - - if (paramType == typeSystem.Boolean) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeBool_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Boolean) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeBoolArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Char) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeChar_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Char) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeCharArray_MethodRef); - continue; - } - - if (paramType == typeSystem.SByte) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyte_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.SByte) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyteArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Byte) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeByte_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Byte) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeByteArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Int16) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeShort_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Int16) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeShortArray_MethodRef); - continue; - } - - if (paramType == typeSystem.UInt16) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshort_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.UInt16) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshortArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Int32) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Int32) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeIntArray_MethodRef); - continue; - } - - if (paramType == typeSystem.UInt32) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUint_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.UInt32) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUintArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Int64) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeLong_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Int64) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeLongArray_MethodRef); - continue; - } - - if (paramType == typeSystem.UInt64) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlong_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.UInt64) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlongArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Single) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeFloat_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Single) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeFloatArray_MethodRef); - continue; - } - - if (paramType == typeSystem.Double) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeDouble_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.Double) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeDoubleArray_MethodRef); - continue; - } - - if (paramType == typeSystem.String) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeString_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType() == typeSystem.String) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeStringArray_MethodRef); - continue; - } - - // Unity primitives (+arrays) - - if (paramType.FullName == CodeGenHelpers.UnityColor_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeColor_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityColor_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeColorArray_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityColor32_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeColor32_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityColor32_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeColor32Array_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityVector2_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector2_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityVector2_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector2Array_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityVector3_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector3_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityVector3_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector3Array_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityVector4_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector4_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityVector4_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeVector4Array_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityQuaternion_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeQuaternion_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityQuaternion_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeQuaternionArray_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityRay_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeRay_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityRay_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeRayArray_MethodRef); - continue; - } - - if (paramType.FullName == CodeGenHelpers.UnityRay2D_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeRay2D_MethodRef); - continue; - } - - if (paramType.IsArray && paramType.GetElementType().FullName == CodeGenHelpers.UnityRay2D_FullName) - { - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeRay2DArray_MethodRef); - continue; - } - - // Enum - - { - var paramEnumIntType = paramType.GetEnumAsInt(); - if (paramEnumIntType != null) - { - if (paramEnumIntType == typeSystem.Int32) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; - } - - if (paramEnumIntType == typeSystem.UInt32) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.UInt32)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUint_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; - } - - if (paramEnumIntType == typeSystem.Byte) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Byte)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeByte_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; - } - - if (paramEnumIntType == typeSystem.SByte) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.SByte)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyte_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; - } - - if (paramEnumIntType == typeSystem.Int16) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int16)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeShort_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; - } - - if (paramEnumIntType == typeSystem.UInt16) + if (!paramType.IsValueType) + { + if (!GetWriteMethodForParameter(typeSystem.Boolean, out var boolMethodRef)) { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.UInt16)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshort_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; + m_Diagnostics.AddError(methodDefinition, $"Couldn't find boolean serializer! Something's wrong!"); + return; } + + methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); + int isSetLocalIndex = methodDefinition.Body.Variables.Count - 1; + + // bool isSet = (param != null); + instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); + instructions.Add(processor.Create(OpCodes.Ldnull)); + instructions.Add(processor.Create(OpCodes.Cgt_Un)); + instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex)); + + // writer.WriteValueSafe(isSet); + instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); + instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex)); + instructions.Add(processor.Create(OpCodes.Call, boolMethodRef)); + + // if(isSet) { + jumpInstruction = processor.Create(OpCodes.Nop); + instructions.Add(processor.Create(OpCodes.Ldloc, isSetLocalIndex)); + instructions.Add(processor.Create(OpCodes.Brfalse, jumpInstruction)); + } - if (paramEnumIntType == typeSystem.Int64) + var foundMethodRef = GetWriteMethodForParameter(paramType, out var methodRef); + if (foundMethodRef) + { + // writer.WriteNetworkSerializable(param) for INetworkSerializable, OR + // writer.WriteNetworkSerializable(param, -1, 0) for INetworkSerializable arrays, OR + // writer.WriteValueSafe(param) for value types, OR + // writer.WriteValueSafe(param, -1, 0) for arrays of value types, OR + // writer.WriteValueSafe(param, false) for strings + instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); + var method = methodRef.Resolve(); + var checkParameter = method.Parameters[0]; + if (checkParameter.ParameterType.Resolve() == + m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve()) { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int64)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeLong_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; + checkParameter = method.Parameters[1]; } - - if (paramEnumIntType == typeSystem.UInt64) + if (checkParameter.IsIn) { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.UInt64)); - int enumLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlong_MethodRef); - - processor.Emit(OpCodes.Ldloc, enumLocalIndex); - processor.Emit(OpCodes.Stloc, localIndex); - continue; + instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1)); } - } - } - - // Enum array - - if (paramType.IsArray) - { - var paramElemEnumIntType = paramType.GetElementType().GetEnumAsInt(); - if (paramElemEnumIntType != null) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int arrLenLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, arrLenLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef); - - var postForInstr = processor.Create(OpCodes.Nop); - - processor.Emit(OpCodes.Ldloc, arrLenLocalIndex); - processor.Emit(OpCodes.Ldc_I4_M1); - processor.Emit(OpCodes.Cgt); - processor.Emit(OpCodes.Brfalse, postForInstr); - - processor.Emit(OpCodes.Ldloc, arrLenLocalIndex); - processor.Emit(OpCodes.Newarr, paramType.GetElementType()); - processor.Emit(OpCodes.Stloc, localIndex); - - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int counterLocalIndex = nhandler.Body.Variables.Count - 1; - - var forBodyInstr = processor.Create(OpCodes.Nop); - var forCheckInstr = processor.Create(OpCodes.Nop); - - processor.Emit(OpCodes.Ldc_I4_0); - processor.Emit(OpCodes.Stloc, counterLocalIndex); - processor.Emit(OpCodes.Br, forCheckInstr); - processor.Append(forBodyInstr); - - if (paramElemEnumIntType == typeSystem.Int32) + else { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef); - - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I4); + instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); } - else if (paramElemEnumIntType == typeSystem.UInt32) + // Special handling for WriteValue() on arrays and strings since they have additional arguments. + if (paramType.IsArray) { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.UInt32)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUint_MethodRef); - - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I4); + instructions.Add(processor.Create(OpCodes.Ldc_I4_M1)); + instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); } - else if (paramElemEnumIntType == typeSystem.Byte) + else if (paramType == typeSystem.String) { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Byte)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeByte_MethodRef); - - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I1); + instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); } - else if (paramElemEnumIntType == typeSystem.SByte) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.SByte)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; + instructions.Add(processor.Create(OpCodes.Call, methodRef)); + } + else + { + m_Diagnostics.AddError(methodDefinition, $"Don't know how to serialize {paramType.Name} - implement INetworkSerializable or add an extension method to FastBufferWriter to define serialization."); + continue; + } - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeSbyte_MethodRef); + if (jumpInstruction != null) + { + // } + instructions.Add(jumpInstruction); + } + } - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I1); - } - else if (paramElemEnumIntType == typeSystem.Int16) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int16)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; + instructions.Add(endInstr); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeShort_MethodRef); + // SendServerRpc(ref serializer, rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc + // SendClientRpc(ref serializer, rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc + if (isServerRpc) + { + // ServerRpc + // SendServerRpc(ref serializer, rpcMethodId, serverRpcParams, rpcDelivery); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I2); - } - else if (paramElemEnumIntType == typeSystem.UInt16) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.UInt16)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; + // serializer + instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUshort_MethodRef); + // rpcMethodId + instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I2); - } - else if (paramElemEnumIntType == typeSystem.Int64) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int64)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; + if (hasRpcParams) + { + // rpcParams + instructions.Add(processor.Create(OpCodes.Ldarg, paramCount)); + } + else + { + // default + instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx)); + } - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeLong_MethodRef); + // rpcDelivery + instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery)); - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I8); - } - else if (paramElemEnumIntType == typeSystem.UInt64) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.UInt64)); - int enumValLocalIndex = nhandler.Body.Variables.Count - 1; + // EndSendServerRpc + instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_SendServerRpc_MethodRef)); + } + else + { + // ClientRpc + // SendClientRpc(ref serializer, rpcMethodId, clientRpcParams, rpcDelivery); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, enumValLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeUlong_MethodRef); + // serializer + instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, enumValLocalIndex); - processor.Emit(OpCodes.Stelem_I8); - } + // rpcMethodId + instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldc_I4_1); - processor.Emit(OpCodes.Add); - processor.Emit(OpCodes.Stloc, counterLocalIndex); - processor.Append(forCheckInstr); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, arrLenLocalIndex); - processor.Emit(OpCodes.Clt); - processor.Emit(OpCodes.Brtrue, forBodyInstr); - - processor.Append(postForInstr); - continue; + if (hasRpcParams) + { + // rpcParams + instructions.Add(processor.Create(OpCodes.Ldarg, paramCount)); + } + else + { + // default + instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx)); } - } - // INetworkSerializable + // rpcDelivery + instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery)); - if (paramType.HasInterface(CodeGenHelpers.INetworkSerializable_FullName)) - { - var paramTypeDef = paramType.Resolve(); - var paramTypeNetworkSerialize_MethodDef = paramTypeDef.Methods.FirstOrDefault(m => m.Name == CodeGenHelpers.INetworkSerializable_NetworkSerialize_Name); - var paramTypeNetworkSerialize_MethodRef = methodDefinition.Module.ImportReference(paramTypeNetworkSerialize_MethodDef); - if (paramTypeNetworkSerialize_MethodRef != null) + // EndSendClientRpc + instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_SendClientRpc_MethodRef)); + } + + { + // End try block + instructions.Add(processor.Create(OpCodes.Leave, lastInstr)); + + // writer.Dispose(); + var handlerFirst = processor.Create(OpCodes.Ldloca, serializerLocIdx); + instructions.Add(handlerFirst); + instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Dispose)); + + // End finally block + instructions.Add(processor.Create(OpCodes.Endfinally)); + + // try { ... serialization code ... } finally { writer.Dispose(); } + var handler = new ExceptionHandler(ExceptionHandlerType.Finally) { - if (paramType.IsValueType) - { - // struct (pass by value) - processor.Emit(OpCodes.Ldloca, localIndex); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Call, paramTypeNetworkSerialize_MethodRef); - } - else - { - // class (pass by reference) - var paramTypeDefCtor = paramTypeDef.GetConstructors().FirstOrDefault(m => m.Parameters.Count == 0); - if (paramTypeDefCtor != null) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); - int isSetLocalIndex = nhandler.Body.Variables.Count - 1; + TryStart = firstInstruction, + TryEnd = handlerFirst, + HandlerStart = handlerFirst, + HandlerEnd = lastInstr + }; + processor.Body.ExceptionHandlers.Add(handler); + } - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, isSetLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeBool_MethodRef); + instructions.Add(lastInstr); + } - var notSetInstr = processor.Create(OpCodes.Nop); + { + var returnInstr = processor.Create(OpCodes.Ret); + var lastInstr = processor.Create(OpCodes.Nop); - processor.Emit(OpCodes.Ldloc, isSetLocalIndex); - processor.Emit(OpCodes.Brfalse, notSetInstr); + // if (__rpc_exec_stage == __RpcExecStage.Server) -> ServerRpc + // if (__rpc_exec_stage == __RpcExecStage.Client) -> ClientRpc + instructions.Add(processor.Create(OpCodes.Ldarg_0)); + instructions.Add(processor.Create(OpCodes.Ldfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)(isServerRpc ? NetworkBehaviour.__RpcExecStage.Server : NetworkBehaviour.__RpcExecStage.Client))); + instructions.Add(processor.Create(OpCodes.Ceq)); + instructions.Add(processor.Create(OpCodes.Brfalse, returnInstr)); - // new INetworkSerializable() - processor.Emit(OpCodes.Newobj, paramTypeDefCtor); - processor.Emit(OpCodes.Stloc, localIndex); + // if (networkManager.IsServer || networkManager.IsHost) -> ServerRpc + // if (networkManager.IsClient || networkManager.IsHost) -> ClientRpc + instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); + instructions.Add(processor.Create(OpCodes.Callvirt, isServerRpc ? m_NetworkManager_getIsServer_MethodRef : m_NetworkManager_getIsClient_MethodRef)); + instructions.Add(processor.Create(OpCodes.Brtrue, lastInstr)); + instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); + instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkManager_getIsHost_MethodRef)); + instructions.Add(processor.Create(OpCodes.Brtrue, lastInstr)); - // INetworkSerializable.NetworkSerialize(serializer) - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Callvirt, paramTypeNetworkSerialize_MethodRef); + instructions.Add(returnInstr); + instructions.Add(lastInstr); + } - processor.Append(notSetInstr); - } - } + instructions.Reverse(); + instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction)); + } - continue; - } - } + private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute) + { + var typeSystem = methodDefinition.Module.TypeSystem; + var nhandler = new MethodDefinition( + $"{methodDefinition.Name}__nhandler", + MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, + methodDefinition.Module.TypeSystem.Void); + nhandler.Parameters.Add(new ParameterDefinition("target", ParameterAttributes.None, m_NetworkBehaviour_TypeRef)); + nhandler.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, m_FastBufferReader_TypeRef.MakeByReferenceType())); + nhandler.Parameters.Add(new ParameterDefinition("rpcParams", ParameterAttributes.None, m_RpcParams_TypeRef)); - // INetworkSerializable[] - if (paramType.IsArray && paramType.GetElementType().HasInterface(CodeGenHelpers.INetworkSerializable_FullName)) + var processor = nhandler.Body.GetILProcessor(); + var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName; + var requireOwnership = true; // default value MUST be = `ServerRpcAttribute.RequireOwnership` + foreach (var attrField in rpcAttribute.Fields) + { + switch (attrField.Name) { - var paramElemType = paramType.GetElementType(); - var paramElemTypeDef = paramElemType.Resolve(); - var paramElemNetworkSerialize_MethodDef = paramElemTypeDef.Methods.FirstOrDefault(m => m.Name == CodeGenHelpers.INetworkSerializable_NetworkSerialize_Name); - var paramElemNetworkSerialize_MethodRef = methodDefinition.Module.ImportReference(paramElemNetworkSerialize_MethodDef); - if (paramElemNetworkSerialize_MethodRef != null) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int arrLenLocalIndex = nhandler.Body.Variables.Count - 1; - - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, arrLenLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeInt_MethodRef); + case k_ServerRpcAttribute_RequireOwnership: + requireOwnership = attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value; + break; + } + } - var postForInstr = processor.Create(OpCodes.Nop); + nhandler.Body.InitLocals = true; + // NetworkManager networkManager; + nhandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef)); + int netManLocIdx = nhandler.Body.Variables.Count - 1; - processor.Emit(OpCodes.Ldloc, arrLenLocalIndex); - processor.Emit(OpCodes.Ldc_I4_M1); - processor.Emit(OpCodes.Cgt); - processor.Emit(OpCodes.Brfalse, postForInstr); + { + var returnInstr = processor.Create(OpCodes.Ret); + var lastInstr = processor.Create(OpCodes.Nop); - processor.Emit(OpCodes.Ldloc, arrLenLocalIndex); - processor.Emit(OpCodes.Newarr, paramElemType); - processor.Emit(OpCodes.Stloc, localIndex); + // networkManager = this.NetworkManager; + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Call, m_NetworkBehaviour_getNetworkManager_MethodRef); + processor.Emit(OpCodes.Stloc, netManLocIdx); - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Int32)); - int counterLocalIndex = nhandler.Body.Variables.Count - 1; + // if (networkManager == null || !networkManager.IsListening) return; + processor.Emit(OpCodes.Ldloc, netManLocIdx); + processor.Emit(OpCodes.Brfalse, returnInstr); + processor.Emit(OpCodes.Ldloc, netManLocIdx); + processor.Emit(OpCodes.Callvirt, m_NetworkManager_getIsListening_MethodRef); + processor.Emit(OpCodes.Brtrue, lastInstr); - var forBodyInstr = processor.Create(OpCodes.Nop); - var forCheckInstr = processor.Create(OpCodes.Nop); + processor.Append(returnInstr); + processor.Append(lastInstr); + } - processor.Emit(OpCodes.Ldc_I4_0); - processor.Emit(OpCodes.Stloc, counterLocalIndex); - processor.Emit(OpCodes.Br, forCheckInstr); - processor.Append(forBodyInstr); + if (isServerRpc && requireOwnership) + { + var roReturnInstr = processor.Create(OpCodes.Ret); + var roLastInstr = processor.Create(OpCodes.Nop); - if (paramElemType.IsValueType) - { - // struct (pass by value) - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldelema, paramElemType); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Call, paramElemNetworkSerialize_MethodRef); - } - else - { - // class (pass by reference) - var paramElemTypeDefCtor = paramElemTypeDef.GetConstructors().FirstOrDefault(m => m.Parameters.Count == 0); - if (paramElemTypeDefCtor != null) - { - nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); - int isSetLocalIndex = nhandler.Body.Variables.Count - 1; + // if (rpcParams.Server.Receive.SenderClientId != target.OwnerClientId) { ... } return; + processor.Emit(OpCodes.Ldarg_2); + processor.Emit(OpCodes.Ldfld, m_RpcParams_Server_FieldRef); + processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_FieldRef); + processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_SenderClientId_FieldRef); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Call, m_NetworkBehaviour_getOwnerClientId_MethodRef); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Ldc_I4, 0); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Brfalse, roLastInstr); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Ldloca, isSetLocalIndex); - processor.Emit(OpCodes.Callvirt, m_NetworkSerializer_SerializeBool_MethodRef); + var logNextInstr = processor.Create(OpCodes.Nop); - var notSetInstr = processor.Create(OpCodes.Nop); + // if (LogLevel.Normal > networkManager.LogLevel) + processor.Emit(OpCodes.Ldloc, netManLocIdx); + processor.Emit(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef); + processor.Emit(OpCodes.Ldc_I4, (int)LogLevel.Normal); + processor.Emit(OpCodes.Cgt); + processor.Emit(OpCodes.Ldc_I4, 0); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Brfalse, logNextInstr); - processor.Emit(OpCodes.Ldloc, isSetLocalIndex); - processor.Emit(OpCodes.Brfalse, notSetInstr); + // Debug.LogError(...); + processor.Emit(OpCodes.Ldstr, "Only the owner can invoke a ServerRpc that requires ownership!"); + processor.Emit(OpCodes.Call, m_Debug_LogError_MethodRef); - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Newobj, paramElemTypeDefCtor); - processor.Emit(OpCodes.Stelem_Ref); + processor.Append(logNextInstr); - processor.Emit(OpCodes.Ldloc, localIndex); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldelem_Ref); - processor.Emit(OpCodes.Ldarg_1); - processor.Emit(OpCodes.Call, paramElemNetworkSerialize_MethodRef); + processor.Append(roReturnInstr); + processor.Append(roLastInstr); + } - processor.Append(notSetInstr); - } - } + // read method parameters from stream + int paramCount = methodDefinition.Parameters.Count; + int[] paramLocalMap = new int[paramCount]; + for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex) + { + var paramDef = methodDefinition.Parameters[paramIndex]; + var paramType = paramDef.ParameterType; - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldc_I4_1); - processor.Emit(OpCodes.Add); - processor.Emit(OpCodes.Stloc, counterLocalIndex); - processor.Append(forCheckInstr); - processor.Emit(OpCodes.Ldloc, counterLocalIndex); - processor.Emit(OpCodes.Ldloc, arrLenLocalIndex); - processor.Emit(OpCodes.Clt); - processor.Emit(OpCodes.Brtrue, forBodyInstr); - - processor.Append(postForInstr); - continue; - } - } + // local variable + nhandler.Body.Variables.Add(new VariableDefinition(paramType)); + int localIndex = nhandler.Body.Variables.Count - 1; + paramLocalMap[paramIndex] = localIndex; // ServerRpcParams, ClientRpcParams { @@ -2640,6 +1255,56 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition continue; } } + + Instruction jumpInstruction = null; + + if (!paramType.IsValueType) + { + if (!GetReadMethodForParameter(typeSystem.Boolean, out var boolMethodRef)) + { + m_Diagnostics.AddError(methodDefinition, $"Couldn't find boolean deserializer! Something's wrong!"); + } + + // reader.ReadValueSafe(out bool isSet) + nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); + int isSetLocalIndex = nhandler.Body.Variables.Count - 1; + processor.Emit(OpCodes.Ldarg_1); + processor.Emit(OpCodes.Ldloca, isSetLocalIndex); + processor.Emit(OpCodes.Call, boolMethodRef); + + // paramType param = null; + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Stloc, localIndex); + + // if(isSet) { + jumpInstruction = processor.Create(OpCodes.Nop); + processor.Emit(OpCodes.Ldloc, isSetLocalIndex); + processor.Emit(OpCodes.Brfalse, jumpInstruction); + } + + var foundMethodRef = GetReadMethodForParameter(paramType, out var methodRef); + if (foundMethodRef) + { + // reader.ReadValueSafe(out localVar); + processor.Emit(OpCodes.Ldarg_1); + processor.Emit(OpCodes.Ldloca, localIndex); + if (paramType == typeSystem.String) + { + processor.Emit(OpCodes.Ldc_I4_0); + } + processor.Emit(OpCodes.Call, methodRef); + } + else + { + m_Diagnostics.AddError(methodDefinition, $"Don't know how to deserialize {paramType.Name} - implement INetworkSerializable or add an extension method to FastBufferReader to define serialization."); + continue; + } + + if (jumpInstruction != null) + { + // } + processor.Append(jumpInstruction); + } } // NetworkBehaviour.__rpc_exec_stage = __RpcExecStage.Server; -> ServerRpc diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index 8281fa8c18..e0bb496d2a 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -26,7 +26,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) m_Diagnostics.Clear(); // read - var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly); + var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly, out var unused); if (assemblyDefinition == null) { m_Diagnostics.AddError($"Cannot read Netcode Runtime assembly definition: {compiledAssembly.Name}"); @@ -87,6 +87,11 @@ private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assem { fieldDefinition.IsPublic = true; } + + if (fieldDefinition.Name == nameof(NetworkManager.RpcReceive)) + { + fieldDefinition.IsPublic = true; + } if (fieldDefinition.Name == nameof(NetworkManager.__rpc_name_table)) { @@ -112,19 +117,6 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) fieldDefinition.IsFamily = true; } } - - foreach (var methodDefinition in typeDefinition.Methods) - { - switch (methodDefinition.Name) - { - case nameof(NetworkBehaviour.__beginSendServerRpc): - case nameof(NetworkBehaviour.__endSendServerRpc): - case nameof(NetworkBehaviour.__beginSendClientRpc): - case nameof(NetworkBehaviour.__endSendClientRpc): - methodDefinition.IsFamily = true; - break; - } - } } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/HashSize.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/HashSize.cs index 7d76c9c48f..c5519f1654 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/HashSize.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/HashSize.cs @@ -5,7 +5,7 @@ namespace Unity.Netcode /// Note that the HashSize does not say anything about the actual final output due to the var int encoding /// It just says how many bytes the maximum will be /// - public enum HashSize + public enum HashSize : byte { /// /// Four byte hash diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs index 74f8abefb5..9431be78b4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using UnityEngine; using System.Linq; +using Unity.Collections; namespace Unity.Netcode { @@ -166,25 +167,27 @@ public class NetworkConfig public string ToBase64() { NetworkConfig config = this; - using var buffer = PooledNetworkBuffer.Get(); - using var writer = PooledNetworkWriter.Get(buffer); - writer.WriteUInt16Packed(config.ProtocolVersion); - writer.WriteInt32Packed(config.TickRate); - writer.WriteInt32Packed(config.ClientConnectionBufferTimeout); - writer.WriteBool(config.ConnectionApproval); - writer.WriteInt32Packed(config.LoadSceneTimeOut); - writer.WriteBool(config.EnableTimeResync); - writer.WriteBool(config.EnsureNetworkVariableLengthSafety); - writer.WriteBits((byte)config.RpcHashSize, 2); - writer.WriteBool(ForceSamePrefabs); - writer.WriteBool(EnableSceneManagement); - writer.WriteBool(RecycleNetworkIds); - writer.WriteSinglePacked(NetworkIdRecycleDelay); - writer.WriteBool(EnableNetworkVariable); - writer.WriteBool(EnableNetworkLogs); - buffer.PadBuffer(); - - return Convert.ToBase64String(buffer.ToArray()); + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(config.ProtocolVersion); + writer.WriteValueSafe(config.TickRate); + writer.WriteValueSafe(config.ClientConnectionBufferTimeout); + writer.WriteValueSafe(config.ConnectionApproval); + writer.WriteValueSafe(config.LoadSceneTimeOut); + writer.WriteValueSafe(config.EnableTimeResync); + writer.WriteValueSafe(config.EnsureNetworkVariableLengthSafety); + writer.WriteValueSafe(config.RpcHashSize); + writer.WriteValueSafe(ForceSamePrefabs); + writer.WriteValueSafe(EnableSceneManagement); + writer.WriteValueSafe(RecycleNetworkIds); + writer.WriteValueSafe(NetworkIdRecycleDelay); + writer.WriteValueSafe(EnableNetworkVariable); + writer.WriteValueSafe(EnableNetworkLogs); + + // Allocates + return Convert.ToBase64String(writer.ToArray()); + } } /// @@ -195,24 +198,24 @@ public void FromBase64(string base64) { NetworkConfig config = this; byte[] binary = Convert.FromBase64String(base64); - using var buffer = new NetworkBuffer(binary); - using var reader = PooledNetworkReader.Get(buffer); - - config.ProtocolVersion = reader.ReadUInt16Packed(); - ushort sceneCount = reader.ReadUInt16Packed(); - config.TickRate = reader.ReadInt32Packed(); - config.ClientConnectionBufferTimeout = reader.ReadInt32Packed(); - config.ConnectionApproval = reader.ReadBool(); - config.LoadSceneTimeOut = reader.ReadInt32Packed(); - config.EnableTimeResync = reader.ReadBool(); - config.EnsureNetworkVariableLengthSafety = reader.ReadBool(); - config.RpcHashSize = (HashSize)reader.ReadBits(2); - config.ForceSamePrefabs = reader.ReadBool(); - config.EnableSceneManagement = reader.ReadBool(); - config.RecycleNetworkIds = reader.ReadBool(); - config.NetworkIdRecycleDelay = reader.ReadSinglePacked(); - config.EnableNetworkVariable = reader.ReadBool(); - config.EnableNetworkLogs = reader.ReadBool(); + using var reader = new FastBufferReader(binary, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out config.ProtocolVersion); + reader.ReadValueSafe(out config.TickRate); + reader.ReadValueSafe(out config.ClientConnectionBufferTimeout); + reader.ReadValueSafe(out config.ConnectionApproval); + reader.ReadValueSafe(out config.LoadSceneTimeOut); + reader.ReadValueSafe(out config.EnableTimeResync); + reader.ReadValueSafe(out config.EnsureNetworkVariableLengthSafety); + reader.ReadValueSafe(out config.RpcHashSize); + reader.ReadValueSafe(out config.ForceSamePrefabs); + reader.ReadValueSafe(out config.EnableSceneManagement); + reader.ReadValueSafe(out config.RecycleNetworkIds); + reader.ReadValueSafe(out config.NetworkIdRecycleDelay); + reader.ReadValueSafe(out config.EnableNetworkVariable); + reader.ReadValueSafe(out config.EnableNetworkLogs); + } } @@ -230,36 +233,36 @@ public ulong GetConfig(bool cache = true) return m_ConfigHash.Value; } - using var buffer = PooledNetworkBuffer.Get(); - using var writer = PooledNetworkWriter.Get(buffer); - - writer.WriteUInt16Packed(ProtocolVersion); - writer.WriteString(NetworkConstants.PROTOCOL_VERSION); - - if (ForceSamePrefabs) + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) { - var sortedDictionary = NetworkPrefabOverrideLinks.OrderBy(x => x.Key); - foreach (var sortedEntry in sortedDictionary) + writer.WriteValueSafe(ProtocolVersion); + writer.WriteValueSafe(NetworkConstants.PROTOCOL_VERSION); + + if (ForceSamePrefabs) + { + var sortedDictionary = NetworkPrefabOverrideLinks.OrderBy(x => x.Key); + foreach (var sortedEntry in sortedDictionary) + { + writer.WriteValueSafe(sortedEntry.Key); + } + } + writer.WriteValueSafe(ConnectionApproval); + writer.WriteValueSafe(EnableNetworkVariable); + writer.WriteValueSafe(ForceSamePrefabs); + writer.WriteValueSafe(EnableSceneManagement); + writer.WriteValueSafe(EnsureNetworkVariableLengthSafety); + writer.WriteValueSafe(RpcHashSize); + + if (cache) { - writer.WriteUInt32Packed(sortedEntry.Key); + m_ConfigHash = XXHash.Hash64(writer.ToArray()); + return m_ConfigHash.Value; } - } - writer.WriteBool(ConnectionApproval); - writer.WriteBool(EnableNetworkVariable); - writer.WriteBool(ForceSamePrefabs); - writer.WriteBool(EnableSceneManagement); - writer.WriteBool(EnsureNetworkVariableLengthSafety); - writer.WriteBits((byte)RpcHashSize, 2); - buffer.PadBuffer(); - - if (cache) - { - m_ConfigHash = XXHash.Hash64(buffer.ToArray()); - return m_ConfigHash.Value; - } - return XXHash.Hash64(buffer.ToArray()); + return XXHash.Hash64(writer.ToArray()); + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 684be7c1e2..b4b3bf9bdb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -5,6 +5,7 @@ using System.Linq; using System.IO; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Messages; namespace Unity.Netcode @@ -16,7 +17,7 @@ public abstract class NetworkBehaviour : MonoBehaviour { #pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `protected` - internal enum __RpcExecStage + public enum __RpcExecStage #pragma warning restore IDE1006 // restore naming rule violation check { None = 0, @@ -24,19 +25,6 @@ internal enum __RpcExecStage Client = 2 } - private static void SetUpdateStage(ref T param) where T : IHasUpdateStage - { - if (param.UpdateStage == NetworkUpdateStage.Unset) - { - param.UpdateStage = NetworkUpdateLoop.UpdateStage; - - if (param.UpdateStage == NetworkUpdateStage.Initialization) - { - param.UpdateStage = NetworkUpdateStage.EarlyUpdate; - } - } - } - #pragma warning disable IDE1006 // disable naming rule violation check // NetworkBehaviourILPP will override this in derived classes to return the name of the concrete type internal virtual string __getTypeName() => nameof(NetworkBehaviour); @@ -46,66 +34,36 @@ private static void SetUpdateStage(ref T param) where T : IHasUpdateStage #pragma warning disable IDE1006 // disable naming rule violation check [NonSerialized] // RuntimeAccessModifiersILPP will make this `protected` - internal __RpcExecStage __rpc_exec_stage = __RpcExecStage.None; + public __RpcExecStage __rpc_exec_stage = __RpcExecStage.None; #pragma warning restore 414 // restore assigned but its value is never used #pragma warning restore IDE1006 // restore naming rule violation check -#pragma warning disable IDE1006 // disable naming rule violation check - // RuntimeAccessModifiersILPP will make this `protected` - internal NetworkSerializer __beginSendServerRpc(uint rpcMethodId, ServerRpcParams serverRpcParams, RpcDelivery rpcDelivery) -#pragma warning restore IDE1006 // restore naming rule violation check + + public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams sendParams, RpcDelivery delivery) { - PooledNetworkWriter writer; - - SetUpdateStage(ref serverRpcParams.Send); - - if (serverRpcParams.Send.UpdateStage == NetworkUpdateStage.Initialization) - { - throw new NotSupportedException( - $"{nameof(NetworkUpdateStage.Initialization)} cannot be used as a target for processing RPCs."); - } - - var messageQueueContainer = NetworkManager.MessageQueueContainer; - var networkDelivery = rpcDelivery == RpcDelivery.Reliable ? NetworkDelivery.ReliableSequenced : NetworkDelivery.UnreliableSequenced; - - if (IsHost) - { - writer = messageQueueContainer.BeginAddQueueItemToFrame(MessageQueueContainer.MessageType.ServerRpc, Time.realtimeSinceStartup, networkDelivery, - NetworkManager.ServerClientId, null, MessageQueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage); - } - else + NetworkDelivery networkDelivery = NetworkDelivery.Reliable; + switch (delivery) { - writer = messageQueueContainer.BeginAddQueueItemToFrame(MessageQueueContainer.MessageType.ServerRpc, Time.realtimeSinceStartup, networkDelivery, - NetworkManager.ServerClientId, null, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - - writer.WriteByte((byte)MessageQueueContainer.MessageType.ServerRpc); - writer.WriteByte((byte)serverRpcParams.Send.UpdateStage); // NetworkUpdateStage + case RpcDelivery.Reliable: + networkDelivery = NetworkDelivery.ReliableSequenced; + break; + case RpcDelivery.Unreliable: + networkDelivery = NetworkDelivery.Unreliable; + break; } - writer.WriteUInt64Packed(NetworkObjectId); // NetworkObjectId - writer.WriteUInt16Packed(NetworkBehaviourId); // NetworkBehaviourId - writer.WriteUInt32Packed(rpcMethodId); // NetworkRpcMethodId - - - return writer.Serializer; - } - -#pragma warning disable IDE1006 // disable naming rule violation check - // RuntimeAccessModifiersILPP will make this `protected` - internal void __endSendServerRpc(NetworkSerializer serializer, uint rpcMethodId, ServerRpcParams serverRpcParams, RpcDelivery rpcDelivery) -#pragma warning restore IDE1006 // restore naming rule violation check - { - if (serializer == null) + var message = new RpcMessage { - return; - } - - SetUpdateStage(ref serverRpcParams.Send); - - var rpcMessageSize = IsHost - ? NetworkManager.MessageQueueContainer.EndAddQueueItemToFrame(serializer.Writer, MessageQueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage) - : NetworkManager.MessageQueueContainer.EndAddQueueItemToFrame(serializer.Writer, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - + Data = new RpcMessage.Metadata + { + Type = RpcMessage.RpcType.Server, + NetworkObjectId = NetworkObjectId, + NetworkBehaviourId = NetworkBehaviourId, + NetworkMethodId = rpcMethodId + }, + RPCData = writer + }; + var rpcMessageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ServerClientId); #if DEVELOPMENT_BUILD || UNITY_EDITOR if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName)) { @@ -119,108 +77,49 @@ internal void __endSendServerRpc(NetworkSerializer serializer, uint rpcMethodId, #endif } -#pragma warning disable IDE1006 // disable naming rule violation check - // RuntimeAccessModifiersILPP will make this `protected` - internal NetworkSerializer __beginSendClientRpc(uint rpcMethodId, ClientRpcParams clientRpcParams, RpcDelivery rpcDelivery) -#pragma warning restore IDE1006 // restore naming rule violation check + public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, ClientRpcParams sendParams, RpcDelivery delivery) { - PooledNetworkWriter writer; - - SetUpdateStage(ref clientRpcParams.Send); - - if (clientRpcParams.Send.UpdateStage == NetworkUpdateStage.Initialization) - { - throw new NotSupportedException( - $"{nameof(NetworkUpdateStage.Initialization)} cannot be used as a target for processing RPCs."); - } - - // This will start a new queue item entry and will then return the writer to the current frame's stream - var networkDelivery = rpcDelivery == RpcDelivery.Reliable ? NetworkDelivery.ReliableSequenced : NetworkDelivery.UnreliableSequenced; - - ulong[] clientIds = clientRpcParams.Send.TargetClientIds ?? NetworkManager.ConnectedClientsIds; - if (clientRpcParams.Send.TargetClientIds != null && clientRpcParams.Send.TargetClientIds.Length == 0) + NetworkDelivery networkDelivery = NetworkDelivery.Reliable; + switch (delivery) { - clientIds = NetworkManager.ConnectedClientsIds; + case RpcDelivery.Reliable: + networkDelivery = NetworkDelivery.ReliableSequenced; + break; + case RpcDelivery.Unreliable: + networkDelivery = NetworkDelivery.Unreliable; + break; } - //NOTES ON BELOW CHANGES: - //The following checks for IsHost and whether the host client id is part of the clients to recieve the RPC - //Is part of a patch-fix to handle looping back RPCs into the next frame's inbound queue. - //!!! This code is temporary and will change (soon) when NetworkSerializer can be configured for mutliple NetworkWriters!!! - var containsServerClientId = clientIds.Contains(NetworkManager.ServerClientId); - bool addHeader = true; - var messageQueueContainer = NetworkManager.MessageQueueContainer; - if (IsHost && containsServerClientId) + var message = new RpcMessage { - //Always write to the next frame's inbound queue - writer = messageQueueContainer.BeginAddQueueItemToFrame(MessageQueueContainer.MessageType.ClientRpc, Time.realtimeSinceStartup, networkDelivery, - NetworkManager.ServerClientId, null, MessageQueueHistoryFrame.QueueFrameType.Inbound, clientRpcParams.Send.UpdateStage); - - //Handle sending to the other clients, if so the above notes explain why this code is here (a temporary patch-fix) - if (clientIds.Length > 1) - { - //Set the loopback frame - messageQueueContainer.SetLoopBackFrameItem(clientRpcParams.Send.UpdateStage); - - //Switch to the outbound queue - writer = messageQueueContainer.BeginAddQueueItemToFrame(MessageQueueContainer.MessageType.ClientRpc, Time.realtimeSinceStartup, networkDelivery, NetworkObjectId, - clientIds, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - } - else + Data = new RpcMessage.Metadata { - addHeader = false; - } + Type = RpcMessage.RpcType.Client, + NetworkObjectId = NetworkObjectId, + NetworkBehaviourId = NetworkBehaviourId, + NetworkMethodId = rpcMethodId + }, + RPCData = writer + }; + int messageSize; + + if (sendParams.Send.TargetClientIds != null) + { + messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIds); + } + else if (sendParams.Send.TargetClientIdsNativeArray != null) + { + // NativeArray doesn't implement required IReadOnlyList interface, but that's ok, pointer + length + // will be more efficient anyway. + messageSize = NetworkManager.SendMessage(message, networkDelivery, + (ulong*) sendParams.Send.TargetClientIdsNativeArray.Value.GetUnsafePtr(), + sendParams.Send.TargetClientIdsNativeArray.Value.Length); } else { - writer = messageQueueContainer.BeginAddQueueItemToFrame(MessageQueueContainer.MessageType.ClientRpc, Time.realtimeSinceStartup, networkDelivery, NetworkObjectId, - clientIds, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); + messageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ConnectedClientsIds); } - if (addHeader) - { - writer.WriteByte((byte)MessageQueueContainer.MessageType.ClientRpc); - writer.WriteByte((byte)clientRpcParams.Send.UpdateStage); // NetworkUpdateStage - } - writer.WriteUInt64Packed(NetworkObjectId); // NetworkObjectId - writer.WriteUInt16Packed(NetworkBehaviourId); // NetworkBehaviourId - writer.WriteUInt32Packed(rpcMethodId); // NetworkRpcMethodId - - - return writer.Serializer; - } - -#pragma warning disable IDE1006 // disable naming rule violation check - // RuntimeAccessModifiersILPP will make this `protected` - internal void __endSendClientRpc(NetworkSerializer serializer, uint rpcMethodId, ClientRpcParams clientRpcParams, RpcDelivery rpcDelivery) -#pragma warning restore IDE1006 // restore naming rule violation check - { - if (serializer == null) - { - return; - } - - SetUpdateStage(ref clientRpcParams.Send); - - if (IsHost) - { - ulong[] clientIds = clientRpcParams.Send.TargetClientIds ?? NetworkManager.ConnectedClientsIds; - if (clientRpcParams.Send.TargetClientIds != null && clientRpcParams.Send.TargetClientIds.Length == 0) - { - clientIds = NetworkManager.ConnectedClientsIds; - } - - var containsServerClientId = clientIds.Contains(NetworkManager.ServerClientId); - if (containsServerClientId && clientIds.Length == 1) - { - NetworkManager.MessageQueueContainer.EndAddQueueItemToFrame(serializer.Writer, MessageQueueHistoryFrame.QueueFrameType.Inbound, clientRpcParams.Send.UpdateStage); - - return; - } - } - - var messageSize = NetworkManager.MessageQueueContainer.EndAddQueueItemToFrame(serializer.Writer, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - #if DEVELOPMENT_BUILD || UNITY_EDITOR if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName)) { @@ -550,50 +449,6 @@ private bool CouldHaveDirtyNetworkVariables() return false; } - internal void WriteNetworkVariableData(Stream stream, ulong clientId) - { - if (NetworkVariableFields.Count == 0) - { - return; - } - - using var writer = PooledNetworkWriter.Get(stream); - for (int j = 0; j < NetworkVariableFields.Count; j++) - { - bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId); - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - if (!canClientRead) - { - writer.WriteUInt16Packed(0); - } - } - else - { - writer.WriteBool(canClientRead); - } - - if (canClientRead) - { - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - using var varBuffer = PooledNetworkBuffer.Get(); - NetworkVariableFields[j].WriteField(varBuffer); - varBuffer.PadBuffer(); - - writer.WriteUInt16Packed((ushort)varBuffer.Length); - varBuffer.CopyTo(stream); - } - else - { - NetworkVariableFields[j].WriteField(stream); - writer.WritePadBits(); - } - } - } - } - internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong clientId) { if (NetworkVariableFields.Count == 0) @@ -634,69 +489,6 @@ internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong client } } } - - internal void SetNetworkVariableData(Stream stream) - { - if (NetworkVariableFields.Count == 0) - { - return; - } - - using var reader = PooledNetworkReader.Get(stream); - for (int j = 0; j < NetworkVariableFields.Count; j++) - { - ushort varSize = 0; - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - varSize = reader.ReadUInt16Packed(); - - if (varSize == 0) - { - continue; - } - } - else - { - if (!reader.ReadBool()) - { - continue; - } - } - - long readStartPos = stream.Position; - - NetworkVariableFields[j].ReadField(stream); - reader.SkipPadBits(); - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - if (stream is NetworkBuffer networkBuffer) - { - networkBuffer.SkipPadBits(); - } - - if (stream.Position > (readStartPos + varSize)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Var data read too far. {stream.Position - (readStartPos + varSize)} bytes."); - } - - stream.Position = readStartPos + varSize; - } - else if (stream.Position < (readStartPos + varSize)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Var data read too little. {(readStartPos + varSize) - stream.Position} bytes."); - } - - stream.Position = readStartPos + varSize; - } - } - } - } internal void SetNetworkVariableData(ref FastBufferReader reader) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 6f4c8395b4..38f383b07a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -26,7 +26,10 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem #pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `public` - internal static readonly Dictionary> __rpc_func_table = new Dictionary>(); + internal delegate void RpcReceive(NetworkBehaviour behaviour, ref FastBufferReader reader, __RpcParams parameters); + + // RuntimeAccessModifiersILPP will make this `public` + internal static readonly Dictionary __rpc_func_table = new Dictionary(); #if DEVELOPMENT_BUILD || UNITY_EDITOR // RuntimeAccessModifiersILPP will make this `public` @@ -41,12 +44,9 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem private static ProfilerMarker s_TransportConnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportConnect"); private static ProfilerMarker s_HandleIncomingData = new ProfilerMarker($"{nameof(NetworkManager)}.{nameof(HandleIncomingData)}"); private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect"); - private static ProfilerMarker s_InvokeRpc = new ProfilerMarker($"{nameof(NetworkManager)}.{nameof(InvokeRpc)}"); #endif private const double k_TimeSyncFrequency = 1.0d; // sync every second, TODO will be removed once timesync is done via snapshots - internal MessageQueueContainer MessageQueueContainer { get; private set; } - internal SnapshotSystem SnapshotSystem { get; private set; } internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; } @@ -223,9 +223,6 @@ public GameObject GetNetworkPrefabOverride(GameObject gameObject) public NetworkSceneManager SceneManager { get; private set; } - // Has to have setter for tests - internal IInternalMessageHandler MessageHandler { get; set; } - /// /// Gets the networkId of the server /// @@ -450,10 +447,6 @@ private void Initialize(bool server) NetworkLog.LogInfo(nameof(Initialize)); } - // Register INetworkUpdateSystem for receiving data from the wire - // Must always be registered before any other systems or messages can end up being re-ordered by frame timing - // Cannot allow any new data to arrive from the wire after MessageQueueContainer's Initialization update - // has run this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); this.RegisterNetworkUpdate(NetworkUpdateStage.PostLateUpdate); @@ -481,8 +474,6 @@ private void Initialize(bool server) BehaviourUpdater = new NetworkBehaviourUpdater(); - // Only create this if it's not already set (like in test cases) - MessageHandler ??= CreateMessageHandler(); if (NetworkMetrics == null) { @@ -531,20 +522,6 @@ private void Initialize(bool server) NetworkTickSystem = new NetworkTickSystem(NetworkConfig.TickRate, 0, 0); NetworkTickSystem.Tick += OnNetworkManagerTick; - // This should never happen, but in the event that it does there should be (at a minimum) a unity error logged. - if (MessageQueueContainer != null) - { - Debug.LogError( - "Init was invoked, but messageQueueContainer was already initialized! (destroying previous instance)"); - MessageQueueContainer.Dispose(); - MessageQueueContainer = null; - } - - // The MessageQueueContainer must be initialized within the Init method ONLY - // It should ONLY be shutdown and destroyed in the Shutdown method (other than just above) - MessageQueueContainer = new MessageQueueContainer(this); - - // Register INetworkUpdateSystem (always register this after messageQueueContainer has been instantiated) this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate); // This is used to remove entries not needed or invalid @@ -969,11 +946,9 @@ public void Shutdown() if (IsServer) { // make sure all messages are flushed before transport disconnect clients - if (MessageQueueContainer != null) + if (m_MessagingSystem != null) { - MessageQueueContainer.ProcessAndFlushMessageQueue( - queueType: MessageQueueContainer.MessageQueueProcessingTypes.Send, - NetworkUpdateStage.PostLateUpdate); // flushing messages in case transport's disconnect + m_MessagingSystem.ProcessSendQueues(); } var disconnectedIds = new HashSet(); @@ -1020,16 +995,8 @@ public void Shutdown() IsServer = false; IsClient = false; - // Unregister INetworkUpdateSystem before shutting down the MessageQueueContainer this.UnregisterAllNetworkUpdates(); - //If an instance of the MessageQueueContainer is still around, then shut it down and remove the reference - if (MessageQueueContainer != null) - { - MessageQueueContainer.Dispose(); - MessageQueueContainer = null; - } - if (SnapshotSystem != null) { SnapshotSystem.Dispose(); @@ -1065,18 +1032,11 @@ public void Shutdown() SceneManager = null; } - if (MessageHandler != null) - { - MessageHandler = null; - } - if (CustomMessagingManager != null) { CustomMessagingManager = null; } - m_MessageBatcher.Shutdown(); - if (BehaviourUpdater != null) { BehaviourUpdater = null; @@ -1289,9 +1249,6 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A } } - private readonly NetworkBuffer m_InputBufferWrapper = new NetworkBuffer(new byte[0]); - private readonly MessageBatcher m_MessageBatcher = new MessageBatcher(); - public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds) where T : INetworkMessage where U : IReadOnlyList @@ -1362,119 +1319,17 @@ public int SendMessage(in T message, NetworkDelivery delivery, ulong clientId internal void HandleIncomingData(ulong clientId, ArraySegment payload, float receiveTime) { - var firstByte = payload.Array[0]; - if (firstByte != 0b11111111) - { - m_MessagingSystem.HandleIncomingData(clientId, payload, receiveTime); - return; - } #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleIncomingData.Begin(); #endif - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo("Unwrapping Data Header"); - } - - m_InputBufferWrapper.SetTarget(payload.Array); - m_InputBufferWrapper.SetLength(payload.Count + payload.Offset); - m_InputBufferWrapper.Position = payload.Offset + 1; - - using var messageStream = m_InputBufferWrapper; - // Client tried to send a network message that was not the connection request before he was accepted. + m_MessagingSystem.HandleIncomingData(clientId, payload, receiveTime); - if (MessageQueueContainer.IsUsingBatching()) - { - m_MessageBatcher.ReceiveItems(messageStream, ReceiveCallback, clientId, receiveTime); - } - else - { - var messageType = (MessageQueueContainer.MessageType)messageStream.ReadByte(); - MessageHandler.MessageReceiveQueueItem(clientId, messageStream, receiveTime, messageType); - NetworkMetrics.TrackNetworkMessageReceived(clientId, MessageQueueContainer.GetMessageTypeName(messageType), payload.Count); - } #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleIncomingData.End(); #endif } - private void ReceiveCallback(NetworkBuffer messageBuffer, MessageQueueContainer.MessageType messageType, ulong clientId, float receiveTime) - { - MessageHandler.MessageReceiveQueueItem(clientId, messageBuffer, receiveTime, messageType); - NetworkMetrics.TrackNetworkMessageReceived(clientId, MessageQueueContainer.GetMessageTypeName(messageType), messageBuffer.Length); - } - - /// - /// Called when an inbound queued RPC is invoked - /// -#pragma warning disable 618 - internal void InvokeRpc(MessageFrameItem item, NetworkUpdateStage networkUpdateStage) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - s_InvokeRpc.Begin(); -#endif - using var reader = PooledNetworkReader.Get(item.NetworkBuffer); - var networkObjectId = reader.ReadUInt64Packed(); - var networkBehaviourId = reader.ReadUInt16Packed(); - var networkMethodId = reader.ReadUInt32Packed(); - - if (__rpc_func_table.ContainsKey(networkMethodId)) - { - if (!SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) - { - return; - } - - var networkObject = SpawnManager.SpawnedObjects[networkObjectId]; - - var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - if (networkBehaviour == null) - { - return; - } - - var rpcParams = new __RpcParams(); - switch (item.MessageType) - { - case MessageQueueContainer.MessageType.ServerRpc: - rpcParams.Server = new ServerRpcParams - { - Receive = new ServerRpcReceiveParams - { - UpdateStage = networkUpdateStage, - SenderClientId = item.NetworkId - } - }; - break; - case MessageQueueContainer.MessageType.ClientRpc: - rpcParams.Client = new ClientRpcParams - { - Receive = new ClientRpcReceiveParams - { - UpdateStage = networkUpdateStage - } - }; - break; - } - - __rpc_func_table[networkMethodId](networkBehaviour, new NetworkSerializer(item.NetworkReader), rpcParams); - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (__rpc_name_table.TryGetValue(networkMethodId, out var rpcMethodName)) - { - NetworkMetrics.TrackRpcReceived( - item.NetworkId, - networkObjectId, - rpcMethodName, - networkBehaviour.__getTypeName(), - item.StreamSize); - } - s_InvokeRpc.End(); -#endif - } - } - /// /// Disconnects the remote client. /// @@ -1609,7 +1464,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? if (createPlayerObject) { var networkObject = SpawnManager.CreateLocalNetworkObject(false, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash, ownerClientId, null, position, rotation); - SpawnManager.SpawnNetworkObjectLocally(networkObject, SpawnManager.GetNetworkObjectId(), false, true, ownerClientId, null, false, false); + SpawnManager.SpawnNetworkObjectLocally(networkObject, SpawnManager.GetNetworkObjectId(), false, true, ownerClientId, false); ConnectedClients[ownerClientId].PlayerObject = networkObject; } @@ -1685,16 +1540,5 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key); } } - - private IInternalMessageHandler CreateMessageHandler() - { - IInternalMessageHandler messageHandler = new InternalMessageHandler(this); - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - messageHandler = new InternalMessageHandlerProfilingDecorator(messageHandler); -#endif - - return messageHandler; - } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 010895dc3b..53a20fd76d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -16,7 +16,7 @@ namespace Unity.Netcode /// [AddComponentMenu("Netcode/" + nameof(NetworkObject), -99)] [DisallowMultipleComponent] - public sealed class NetworkObject : MonoBehaviour + public sealed class NetworkObject : MonoBehaviour, INetworkSerializable { [HideInInspector] [SerializeField] @@ -60,10 +60,12 @@ internal void GenerateGlobalObjectIdHash() /// internal NetworkManager NetworkManagerOwner; + private ulong m_NetworkObjectId; + /// /// Gets the unique Id of this object that is synced across the network /// - public ulong NetworkObjectId { get; internal set; } + public ulong NetworkObjectId { get => m_NetworkObjectId; internal set => m_NetworkObjectId = value; } /// /// Gets the ClientId of the owner of this NetworkObject @@ -465,7 +467,7 @@ private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool pla throw new NotServerException($"Only server can spawn {nameof(NetworkObject)}s"); } - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, null, false, destroyWithScene); + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, destroyWithScene); if (NetworkManager.NetworkConfig.UseSnapshotSpawn) { @@ -570,36 +572,6 @@ internal void SetCachedParent(Transform parentTransform) m_CachedParent = parentTransform; } - internal static void WriteNetworkParenting(NetworkWriter writer, bool isReparented, ulong? latestParent) - { - writer.WriteBool(isReparented); - if (isReparented) - { - var isLatestParentSet = latestParent != null && latestParent.HasValue; - writer.WriteBool(isLatestParentSet); - if (isLatestParentSet) - { - writer.WriteUInt64Packed(latestParent.Value); - } - } - } - - internal static (bool IsReparented, ulong? LatestParent) ReadNetworkParenting(NetworkReader reader) - { - ulong? latestParent = null; - bool isReparented = reader.ReadBool(); - if (isReparented) - { - var isLatestParentSet = reader.ReadBool(); - if (isLatestParentSet) - { - latestParent = reader.ReadUInt64Packed(); - } - } - - return (isReparented, latestParent); - } - internal (bool IsReparented, ulong? LatestParent) GetNetworkParenting() => (m_IsReparented, m_LatestParent); internal void SetNetworkParenting(bool isReparented, ulong? latestParent) @@ -856,16 +828,6 @@ internal List ChildNetworkBehaviours } } - internal void WriteNetworkVariableData(Stream stream, ulong clientId) - { - for (int i = 0; i < ChildNetworkBehaviours.Count; i++) - { - var behavior = ChildNetworkBehaviours[i]; - behavior.InitializeVariables(); - behavior.WriteNetworkVariableData(stream, clientId); - } - } - internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong clientId) { for (int i = 0; i < ChildNetworkBehaviours.Count; i++) @@ -876,16 +838,6 @@ internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong client } } - internal void SetNetworkVariableData(Stream stream) - { - for (int i = 0; i < ChildNetworkBehaviours.Count; i++) - { - var behaviour = ChildNetworkBehaviours[i]; - behaviour.InitializeVariables(); - behaviour.SetNetworkVariableData(stream); - } - } - internal void SetNetworkVariableData(ref FastBufferReader reader) { for (int i = 0; i < ChildNetworkBehaviours.Count; i++) @@ -1118,182 +1070,6 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) return obj; } - /// - /// Used to serialize a NetworkObjects during scene synchronization that occurs - /// upon a client being approved or a scene transition. - /// - /// writer into the outbound stream - /// clientid we are targeting - internal void SerializeSceneObject(NetworkWriter writer, ulong targetClientId) - { - writer.WriteBool(IsPlayerObject); - writer.WriteUInt64Packed(NetworkObjectId); - writer.WriteUInt64Packed(OwnerClientId); - - NetworkObject parentNetworkObject = null; - - if (!AlwaysReplicateAsRoot && transform.parent != null) - { - parentNetworkObject = transform.parent.GetComponent(); - } - - if (parentNetworkObject == null) - { - // We don't have a parent - writer.WriteBool(false); - } - else - { - // We do have a parent - writer.WriteBool(true); - // Write the parent's NetworkObjectId to be used for linking back to the child - writer.WriteUInt64Packed(parentNetworkObject.NetworkObjectId); - } - - // Write if we are a scene object or not - writer.WriteBool(IsSceneObject ?? true); - - // Write the hash for this NetworkObject - writer.WriteUInt32Packed(HostCheckForGlobalObjectIdHashOverride()); - - if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId)) - { - // Set the position and rotation data marker to true (i.e. flag to know, when reading from the stream, that position and rotation data follows). - writer.WriteBool(true); - - // Write position - writer.WriteSinglePacked(transform.position.x); - writer.WriteSinglePacked(transform.position.y); - writer.WriteSinglePacked(transform.position.z); - - // Write rotation - writer.WriteSinglePacked(transform.rotation.eulerAngles.x); - writer.WriteSinglePacked(transform.rotation.eulerAngles.y); - writer.WriteSinglePacked(transform.rotation.eulerAngles.z); - } - else - { - // Set the position and rotation data marker to false (i.e. flag to know, when reading from the stream, that position and rotation data *was not included*) - writer.WriteBool(false); - } - - { - var (isReparented, latestParent) = GetNetworkParenting(); - WriteNetworkParenting(writer, isReparented, latestParent); - } - - // Write whether we are including network variable data - writer.WriteBool(NetworkManager.NetworkConfig.EnableNetworkVariable); - - //If we are including NetworkVariable data - if (NetworkManager.NetworkConfig.EnableNetworkVariable) - { - var buffer = writer.GetStream() as NetworkBuffer; - - // Write placeholder size, NOT as a packed value, initially as zero (i.e. we do not know how much NetworkVariable data will be written yet) - writer.WriteUInt32(0); - - // Mark our current position before we potentially write any NetworkVariable data - var positionBeforeNetworkVariableData = buffer.Position; - - // Write network variable data - WriteNetworkVariableData(buffer, targetClientId); - - // If our current buffer position is greater than our positionBeforeNetworkVariableData then we wrote NetworkVariable data - // Part 1: This will include the total NetworkVariable data size, if there was NetworkVariable data written, to the stream - // in order to be able to skip past this entry on the deserialization side in the event this NetworkObject fails to be - // constructed (See Part 2 below in the DeserializeSceneObject method) - if (buffer.Position > positionBeforeNetworkVariableData) - { - // Store our current stream buffer position - var endOfNetworkVariableData = buffer.Position; - - // Calculate the total NetworkVariable data size written - var networkVariableDataSize = endOfNetworkVariableData - positionBeforeNetworkVariableData; - - // Move the stream position back to just before we wrote our size (we include the unpacked UInt32 data size placeholder) - buffer.Position = positionBeforeNetworkVariableData - sizeof(uint); - - // Now write the actual data size written into our unpacked UInt32 placeholder position - writer.WriteUInt32((uint)(networkVariableDataSize)); - - // Finally, revert the buffer position back to the end of the network variable data written - buffer.Position = endOfNetworkVariableData; - } - } - } - - /// - /// Used to deserialize a serialized scene object which occurs - /// when the client is approved or during a scene transition - /// - /// inbound stream - /// reader for the stream - /// NetworkManager instance - /// optional to use NetworkObject deserialized - internal static NetworkObject DeserializeSceneObject(NetworkBuffer objectStream, NetworkReader reader, NetworkManager networkManager) - { - var isPlayerObject = reader.ReadBool(); - var networkId = reader.ReadUInt64Packed(); - var ownerClientId = reader.ReadUInt64Packed(); - var hasParent = reader.ReadBool(); - ulong? parentNetworkId = null; - - if (hasParent) - { - parentNetworkId = reader.ReadUInt32Packed(); - } - - bool isSceneObject = reader.ReadBool(); - - uint globalObjectIdHash = reader.ReadUInt32Packed(); - Vector3? position = null; - Quaternion? rotation = null; - - // Check to see if we have position and rotation values that follows - if (reader.ReadBool()) - { - position = new Vector3(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); - rotation = Quaternion.Euler(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); - } - - var (isReparented, latestParent) = ReadNetworkParenting(reader); - - //Attempt to create a local NetworkObject - var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(isSceneObject, globalObjectIdHash, ownerClientId, parentNetworkId, position, rotation, isReparented); - - networkObject?.SetNetworkParenting(isReparented, latestParent); - - // Determine if this NetworkObject has NetworkVariable data to read - var networkVariableDataIsIncluded = reader.ReadBool(); - - if (networkVariableDataIsIncluded) - { - // (See Part 1 above in the NetworkObject.SerializeSceneObject method to better understand this) - // Part 2: This makes sure that if one NetworkObject fails to construct (for whatever reason) then we can "skip past" - // that specific NetworkObject but continue processing any remaining serialized NetworkObjects as opposed to just - // throwing an exception and skipping the remaining (if any) NetworkObjects. This will prevent one misconfigured - // issue (or more) from breaking the entire loading process. - var networkVariableDataSize = reader.ReadUInt32(); - if (networkObject == null) - { - // Log the error that the NetworkObject failed to construct - Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {globalObjectIdHash}."); - - // If we failed to load this NetworkObject, then skip past the network variable data - objectStream.Position += networkVariableDataSize; - - // We have nothing left to do here. - return null; - } - } - - // Spawn the NetworkObject - networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, networkId, isSceneObject, isPlayerObject, ownerClientId, objectStream, true, false); - - return networkObject; - } - /// /// Used to deserialize a serialized scene object which occurs /// when the client is approved or during a scene transition @@ -1356,5 +1132,10 @@ internal uint HostCheckForGlobalObjectIdHashOverride() return GlobalObjectIdHash; } + + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + { + serializer.SerializeValue(ref m_NetworkObjectId); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index 7270028abe..03acb8ef41 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -3,6 +3,7 @@ using System.IO; using PlasticGui.Configuration; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Messages; using UnityEditor.VersionControl; using UnityEngine; @@ -93,7 +94,6 @@ internal class Snapshot internal SnapshotDespawnCommand[] Despawns; internal int NumDespawns = 0; - private MemoryStream m_BufferStream; internal NetworkManager NetworkManager; // indexed by ObjectId @@ -108,7 +108,6 @@ internal class Snapshot /// Whether this Snapshot uses the tick as an index internal Snapshot() { - m_BufferStream = new MemoryStream(RecvBuffer, 0, k_BufferSize); // we ask for twice as many slots because there could end up being one free spot between each pair of slot used Allocator = new IndexAllocator(k_BufferSize, k_MaxVariables * 2); Spawns = new SnapshotSpawnCommand[m_MaxSpawns]; @@ -404,11 +403,22 @@ internal void ReadIndex(in SnapshotDataMessage message) var networkVariable = FindNetworkVar(Entries[pos].Key); if (networkVariable != null) { - m_BufferStream.Seek(Entries[pos].Position, SeekOrigin.Begin); - // todo: consider refactoring out in its own function to accomodate - // other ways to (de)serialize - // Not using keepDirtyDelta anymore which is great. todo: remove and check for the overall effect on > 2 player - networkVariable.ReadDelta(m_BufferStream, false); + unsafe + { + // This avoids copies - using Allocator.None creates a direct memory view into the buffer. + fixed (byte* buffer = RecvBuffer) + { + var reader = new FastBufferReader(buffer, Collections.Allocator.None, RecvBuffer.Length); + using (reader) + { + reader.Seek(Entries[pos].Position); + // todo: consider refactoring out in its own function to accomodate + // other ways to (de)serialize + // Not using keepDirtyDelta anymore which is great. todo: remove and check for the overall effect on > 2 player + networkVariable.ReadDelta(ref reader, false); + } + } + } } } } @@ -436,12 +446,12 @@ internal void ReadSpawns(in SnapshotDataMessage message) if (spawnCommand.ParentNetworkId == spawnCommand.NetworkObjectId) { var networkObject = NetworkManager.SpawnManager.CreateLocalNetworkObject(false, spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, null, spawnCommand.ObjectPosition, spawnCommand.ObjectRotation); - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, null, false, false); + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, false); } else { var networkObject = NetworkManager.SpawnManager.CreateLocalNetworkObject(false, spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, spawnCommand.ParentNetworkId, spawnCommand.ObjectPosition, spawnCommand.ObjectRotation); - NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, null, false, false); + NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, false); } } for (var i = 0; i < message.Despawns.Length; i++) @@ -713,8 +723,6 @@ private void SendSnapshot(ulong clientId) m_ConnectionRtts[clientId].NotifySend(m_ClientData[clientId].SequenceNumber, Time.unscaledTime); - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.SnapshotData, NetworkDelivery.Unreliable, new[] { clientId }, NetworkUpdateLoop.UpdateStage); - var sequence = m_ClientData[clientId].SequenceNumber; var message = new SnapshotDataMessage { @@ -739,6 +747,8 @@ private void SendSnapshot(ulong clientId) WriteIndex(ref message); WriteSpawns(ref message, clientId); + m_NetworkManager.SendMessage(message, NetworkDelivery.Unreliable, clientId); + m_ClientData[clientId].LastReceivedSequence = 0; // todo: this is incorrect (well, sub-optimal) @@ -932,19 +942,24 @@ internal void Store(ulong networkObjectId, int behaviourIndex, int variableIndex WriteVariableToSnapshot(m_Snapshot, networkVariable, pos); } - private void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBase networkVariable, int index) + private unsafe void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBase networkVariable, int index) { // write var into buffer, possibly adjusting entry's position and Length - using var varBuffer = PooledNetworkBuffer.Get(); - networkVariable.WriteDelta(varBuffer); - if (varBuffer.Length > snapshot.Entries[index].Length) + var varBuffer = new FastBufferWriter(1300, Allocator.Temp); + using (varBuffer) { - // allocate this Entry's buffer - snapshot.AllocateEntry(ref snapshot.Entries[index], index, (int)varBuffer.Length); - } + networkVariable.WriteDelta(ref varBuffer); + if (varBuffer.Length > snapshot.Entries[index].Length) + { + // allocate this Entry's buffer + snapshot.AllocateEntry(ref snapshot.Entries[index], index, (int)varBuffer.Length); + } - // Copy the serialized NetworkVariable into our buffer - Buffer.BlockCopy(varBuffer.GetBuffer(), 0, snapshot.MainBuffer, snapshot.Entries[index].Position, (int)varBuffer.Length); + fixed (byte* buffer = snapshot.MainBuffer) + { + UnsafeUtility.MemCpy(buffer + snapshot.Entries[index].Position, varBuffer.GetUnsafePtr(), varBuffer.Length); + } + } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index 2a7b0e1314..834300d0c3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using Unity.Netcode.Messages; namespace Unity.Netcode { @@ -22,17 +23,17 @@ internal CustomMessagingManager(NetworkManager networkManager) /// /// The clientId that sent the message /// The stream containing the message data - public delegate void UnnamedMessageDelegate(ulong clientId, Stream stream); + public delegate void UnnamedMessageDelegate(ulong clientId, ref FastBufferReader reader); /// /// Event invoked when unnamed messages arrive /// public event UnnamedMessageDelegate OnUnnamedMessage; - internal void InvokeUnnamedMessage(ulong clientId, Stream stream) + internal void InvokeUnnamedMessage(ulong clientId, ref FastBufferReader reader) { - OnUnnamedMessage?.Invoke(clientId, stream); - m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, stream.SafeGetLengthOrDefault()); + OnUnnamedMessage?.Invoke(clientId, ref reader); + m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, reader.Length); } /// @@ -41,22 +42,20 @@ internal void InvokeUnnamedMessage(ulong clientId, Stream stream) /// The clients to send to, sends to everyone if null /// The message stream containing the data /// The delivery type (QoS) to send data with - public void SendUnnamedMessage(List clientIds, NetworkBuffer messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) + public void SendUnnamedMessage(List clientIds, ref FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) { if (!m_NetworkManager.IsServer) { throw new InvalidOperationException("Can not send unnamed messages to multiple users as a client"); } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.UnnamedMessage, networkDelivery, clientIds.ToArray(), NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new UnnamedMessage { - using var nonNullContext = (InternalCommandContext)context; - messageBuffer.Position = 0; - messageBuffer.CopyTo(nonNullContext.NetworkWriter.GetStream()); - } + Data = messageBuffer + }; + var size = m_NetworkManager.SendMessage(message, networkDelivery, clientIds); - m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientIds, messageBuffer.Length); + m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientIds, size); } /// @@ -65,22 +64,20 @@ public void SendUnnamedMessage(List clientIds, NetworkBuffer messageBuffe /// The client to send the message to /// The message stream containing the data /// The delivery type (QoS) to send data with - public void SendUnnamedMessage(ulong clientId, NetworkBuffer messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) + public void SendUnnamedMessage(ulong clientId, ref FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) { - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.UnnamedMessage, networkDelivery, new[] { clientId }, NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new UnnamedMessage { - using var nonNullContext = (InternalCommandContext)context; - m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientId, messageBuffer.Position); - messageBuffer.Position = 0; - messageBuffer.CopyTo(nonNullContext.NetworkWriter.GetStream()); - } + Data = messageBuffer + }; + var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId); + m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientId, size); } /// /// Delegate used to handle named messages /// - public delegate void HandleNamedMessageDelegate(ulong senderClientId, Stream messagePayload); + public delegate void HandleNamedMessageDelegate(ulong senderClientId, ref FastBufferReader messagePayload); private Dictionary m_NamedMessageHandlers32 = new Dictionary(); private Dictionary m_NamedMessageHandlers64 = new Dictionary(); @@ -88,22 +85,22 @@ public void SendUnnamedMessage(ulong clientId, NetworkBuffer messageBuffer, Netw private Dictionary m_MessageHandlerNameLookup32 = new Dictionary(); private Dictionary m_MessageHandlerNameLookup64 = new Dictionary(); - internal void InvokeNamedMessage(ulong hash, ulong sender, Stream stream) + internal void InvokeNamedMessage(ulong hash, ulong sender, ref FastBufferReader reader) { - var bytesCount = stream.SafeGetLengthOrDefault(); + var bytesCount = reader.Length; if (m_NetworkManager == null) { // We dont know what size to use. Try every (more collision prone) if (m_NamedMessageHandlers32.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler32)) { - messageHandler32(sender, stream); + messageHandler32(sender, ref reader); m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup32[hash], bytesCount); } if (m_NamedMessageHandlers64.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler64)) { - messageHandler64(sender, stream); + messageHandler64(sender, ref reader); m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup64[hash], bytesCount); } } @@ -115,14 +112,14 @@ internal void InvokeNamedMessage(ulong hash, ulong sender, Stream stream) case HashSize.VarIntFourBytes: if (m_NamedMessageHandlers32.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler32)) { - messageHandler32(sender, stream); + messageHandler32(sender, ref reader); m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup32[hash], bytesCount); } break; case HashSize.VarIntEightBytes: if (m_NamedMessageHandlers64.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler64)) { - messageHandler64(sender, stream); + messageHandler64(sender, ref reader); m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup64[hash], bytesCount); } break; @@ -170,7 +167,7 @@ public void UnregisterNamedMessageHandler(string name) /// The client to send the message to /// The message stream containing the data /// The delivery type (QoS) to send data with - public void SendNamedMessage(string messageName, ulong clientId, Stream messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) + public void SendNamedMessage(string messageName, ulong clientId, ref FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) { ulong hash = 0; switch (m_NetworkManager.NetworkConfig.RpcHashSize) @@ -183,22 +180,13 @@ public void SendNamedMessage(string messageName, ulong clientId, Stream messageS break; } - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(MessageQueueContainer.MessageType.NamedMessage, networkDelivery, new[] { clientId }, NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new NamedMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - nonNullContext.NetworkWriter.WriteUInt64Packed(hash); - - messageStream.Position = 0; - messageStream.CopyTo(nonNullContext.NetworkWriter.GetStream()); - - var size = bufferSizeCapture.StopMeasureSegment(); - - m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientId, messageName, size); - } + Hash = hash, + Data = messageStream + }; + var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId); + m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientId, messageName, size); } /// @@ -208,7 +196,7 @@ public void SendNamedMessage(string messageName, ulong clientId, Stream messageS /// The clients to send to, sends to everyone if null /// The message stream containing the data /// The delivery type (QoS) to send data with - public void SendNamedMessage(string messageName, List clientIds, Stream messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) + public void SendNamedMessage(string messageName, List clientIds, ref FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) { if (!m_NetworkManager.IsServer) { @@ -225,24 +213,13 @@ public void SendNamedMessage(string messageName, List clientIds, Stream m hash = XXHash.Hash64(messageName); break; } - - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - MessageQueueContainer.MessageType.NamedMessage, networkDelivery, - clientIds.ToArray(), NetworkUpdateLoop.UpdateStage); - if (context != null) + var message = new NamedMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - nonNullContext.NetworkWriter.WriteUInt64Packed(hash); - - messageStream.Position = 0; - messageStream.CopyTo(nonNullContext.NetworkWriter.GetStream()); - - var size = bufferSizeCapture.StopMeasureSegment(); - m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientIds, messageName, size); - } + Hash = hash, + Data = messageStream + }; + var size = m_NetworkManager.SendMessage(message, networkDelivery, clientIds); + m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientIds, messageName, size); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs deleted file mode 100644 index 3c82d76341..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.IO; - -namespace Unity.Netcode -{ - internal interface IInternalMessageHandler - { - NetworkManager NetworkManager { get; } - void HandleSceneEvent(ulong clientId, Stream stream); - void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType); - void HandleUnnamedMessage(ulong clientId, Stream stream); - void HandleNamedMessage(ulong clientId, Stream stream); - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs.meta deleted file mode 100644 index 2efca3c244..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 55d4eb01eae74f4bb603dc1d11897d48 -timeCreated: 1617912807 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs deleted file mode 100644 index 79a38489ee..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Linq; - -namespace Unity.Netcode -{ - /// - /// A context used for building an internal command. - /// - internal struct InternalCommandContext : IDisposable - { - public PooledNetworkWriter NetworkWriter; - - private ulong[] m_ClientIds; - private NetworkUpdateStage m_UpdateStage; - private MessageQueueContainer m_Owner; - - public InternalCommandContext(PooledNetworkWriter writer, ulong[] clientIds, NetworkUpdateStage updateStage, MessageQueueContainer owner) - { - NetworkWriter = writer; - m_ClientIds = clientIds; - m_UpdateStage = updateStage; - m_Owner = owner; - } - - public void Dispose() - { - Cleanup(); - } - - public void Cleanup() - { - if (m_Owner.NetworkManager.IsHost) - { - var containsServerClientId = m_ClientIds.Contains(m_Owner.NetworkManager.ServerClientId); - if (containsServerClientId && m_ClientIds.Length == 1) - { - m_Owner.EndAddQueueItemToFrame(NetworkWriter, MessageQueueHistoryFrame.QueueFrameType.Inbound, m_UpdateStage); - return; - } - } - - m_Owner.EndAddQueueItemToFrame(NetworkWriter, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs deleted file mode 100644 index 493aabd475..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.IO; -using UnityEngine; - - -namespace Unity.Netcode -{ - internal class InternalMessageHandler : IInternalMessageHandler - { - public NetworkManager NetworkManager => m_NetworkManager; - private NetworkManager m_NetworkManager; - - public InternalMessageHandler(NetworkManager networkManager) - { - m_NetworkManager = networkManager; - } - - /// - /// Called for all Scene Management related events - /// - /// - /// - public void HandleSceneEvent(ulong clientId, Stream stream) - { - NetworkManager.SceneManager.HandleSceneEvent(clientId, stream); - } - - /// - /// Converts the stream to a PerformanceQueueItem and adds it to the receive queue - /// - public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType) - { - if (NetworkManager.IsServer && clientId == NetworkManager.ServerClientId) - { - return; - } - - if (messageType == MessageQueueContainer.MessageType.None) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"Message header contained an invalid type: {((int)messageType).ToString()}"); - } - - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"Data Header: {nameof(messageType)}={((int)messageType).ToString()}"); - } - - if (NetworkManager.PendingClients.TryGetValue(clientId, out PendingClient client) && (client.ConnectionState == PendingClient.State.PendingApproval || client.ConnectionState == PendingClient.State.PendingConnection)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Message received from {nameof(clientId)}={clientId.ToString()} before it has been accepted"); - } - - return; - } - - var messageQueueContainer = NetworkManager.MessageQueueContainer; - messageQueueContainer.AddQueueItemToInboundFrame(messageType, receiveTime, clientId, (NetworkBuffer)stream); - } - - public void HandleUnnamedMessage(ulong clientId, Stream stream) - { - NetworkManager.CustomMessagingManager.InvokeUnnamedMessage(clientId, stream); - } - - public void HandleNamedMessage(ulong clientId, Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - ulong hash = reader.ReadUInt64Packed(); - - NetworkManager.CustomMessagingManager.InvokeNamedMessage(hash, clientId, stream); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs deleted file mode 100644 index 7f4b1aa268..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; - -namespace Unity.Netcode -{ - internal class MessageBatcher - { - public class SendStream - { - public NetworkDelivery Delivery; - public PooledNetworkBuffer Buffer; - public PooledNetworkWriter Writer; - public bool IsEmpty = true; - - public SendStream() - { - Buffer = PooledNetworkBuffer.Get(); - Writer = PooledNetworkWriter.Get(Buffer); - } - } - - // Stores the stream of batched RPC to send to each client, by ClientId - private readonly Dictionary m_SendDict = new Dictionary(); - - public void Shutdown() - { - foreach (var kvp in m_SendDict) - { - kvp.Value.Writer.Dispose(); - kvp.Value.Buffer.Dispose(); - } - - m_SendDict.Clear(); - } - - - // Used to mark longer lengths. Works because we can't have zero-sized messages - private const byte k_LongLenMarker = 0; - - internal static void PushLength(int length, ref PooledNetworkWriter writer) - { - // If length is single byte we write it - if (length < 256) - { - writer.WriteByte((byte)length); // write the amounts of bytes that are coming up - } - else - { - // otherwise we write a two-byte length - writer.WriteByte(k_LongLenMarker); // mark larger size - writer.WriteByte((byte)(length % 256)); // write the length modulo 256 - writer.WriteByte((byte)(length / 256)); // write the length divided by 256 - } - } - - private int PopLength(in NetworkBuffer messageBuffer) - { - int read = messageBuffer.ReadByte(); - // if we read a non-zero value, we have a single byte length - // or a -1 error we can return - if (read != k_LongLenMarker) - { - return read; - } - - // otherwise, a two-byte length follows. We'll read in len1, len2 - int len1 = messageBuffer.ReadByte(); - if (len1 < 0) - { - // pass errors back to caller - return len1; - } - - int len2 = messageBuffer.ReadByte(); - if (len2 < 0) - { - // pass errors back to caller - return len2; - } - - return len1 + len2 * 256; - } - - /// - /// Add a FrameQueueItem to be sent - /// - public void QueueItem( - IReadOnlyCollection targetList, - in MessageFrameItem item, - int automaticSendThresholdBytes, - SendCallbackType sendCallback) - { - foreach (ulong clientId in targetList) - { - if (!m_SendDict.ContainsKey(clientId)) - { - // todo: consider what happens if many clients join and leave the game consecutively - // we probably need a cleanup mechanism at some point - m_SendDict[clientId] = new SendStream(); - m_SendDict[clientId].Writer.WriteByte(0b11111111); - } - - SendStream sendStream = m_SendDict[clientId]; - - if (sendStream.IsEmpty) - { - sendStream.IsEmpty = false; - sendStream.Delivery = item.Delivery; - } - // If the item is a different channel we have to flush and change channels. - // This isn't great if channels are interleaved, but having a different stream - // per channel would create ordering issues. - else if (sendStream.Delivery != item.Delivery) - { - sendCallback(clientId, sendStream); - // clear the batch that was sent from the SendDict - sendStream.Buffer.SetLength(0); - sendStream.Buffer.Position = 0; - sendStream.Writer.WriteByte(0b11111111); - - sendStream.Delivery = item.Delivery; - } - - // write the amounts of bytes that are coming up - PushLength(item.MessageData.Count, ref sendStream.Writer); - - // write the message to send - sendStream.Writer.WriteBytes(item.MessageData.Array, item.MessageData.Count, item.MessageData.Offset); - - if (sendStream.Buffer.Length >= automaticSendThresholdBytes) - { - sendCallback(clientId, sendStream); - // clear the batch that was sent from the SendDict - sendStream.Buffer.SetLength(0); - sendStream.Buffer.Position = 0; - sendStream.Writer.WriteByte(0b11111111); - sendStream.IsEmpty = true; - } - } - } - - public delegate void SendCallbackType(ulong clientId, SendStream messageStream); - public delegate void ReceiveCallbackType(NetworkBuffer messageStream, MessageQueueContainer.MessageType messageType, ulong clientId, float receiveTime); - - /// - /// Send any batch of messages that are of length above threshold - /// - /// the threshold in bytes - /// the function to call for sending the batch - public void SendItems(int thresholdBytes, SendCallbackType sendCallback) - { - foreach (KeyValuePair entry in m_SendDict) - { - if (!entry.Value.IsEmpty) - { - // read the queued message - int length = (int)m_SendDict[entry.Key].Buffer.Length; - - if (length >= thresholdBytes) - { - sendCallback(entry.Key, entry.Value); - // clear the batch that was sent from the SendDict - entry.Value.Buffer.SetLength(0); - entry.Value.Buffer.Position = 0; - entry.Value.Writer.WriteByte(0b11111111); - entry.Value.IsEmpty = true; - } - } - } - } - - /// - /// Process the messageStream and call the callback with individual messages - /// - /// the messageStream containing the batched messages - /// the callback to call has type int f(message, type, clientId, time) - /// the clientId to pass back to callback - /// the packet receive time to pass back to callback - public void ReceiveItems(in NetworkBuffer messageBuffer, ReceiveCallbackType receiveCallback, ulong clientId, float receiveTime) - { - using var copy = PooledNetworkBuffer.Get(); - do - { - // read the length of the next message - int messageSize = PopLength(messageBuffer); - if (messageSize < 0) - { - // abort if there's an error reading lengths - return; - } - - // copy what comes after current stream position - long position = messageBuffer.Position; - copy.SetLength(messageSize); - copy.Position = 0; - Buffer.BlockCopy(messageBuffer.GetBuffer(), (int)position, copy.GetBuffer(), 0, messageSize); - - var messageType = (MessageQueueContainer.MessageType)copy.ReadByte(); - receiveCallback(copy, messageType, clientId, receiveTime); - - // seek over the message - // MessageReceiveQueueItem peeks at content, it doesn't advance - messageBuffer.Seek(messageSize, SeekOrigin.Current); - } while (messageBuffer.Position < messageBuffer.Length); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs deleted file mode 100644 index d5acb5e836..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.IO; - -namespace Unity.Netcode -{ - /// - /// MessageFrameItem - /// Container structure for messages written to the Queue Frame - /// Used for both Inbound and Outbound messages - /// NOTE: This structure will change in the near future and is in a state of flux. - /// This will include removing specific properties or changing property types - /// - internal struct MessageFrameItem - { - public NetworkUpdateStage UpdateStage; - public MessageQueueContainer.MessageType MessageType; - /// - /// Sender's network Identifier, or recipient identifier for server RPCs - /// - public ulong NetworkId; - public NetworkDelivery Delivery; - /// - /// Everything other than server RPCs - /// - public ulong[] ClientNetworkIds; - public long StreamSize; - public float Timestamp; - public PooledNetworkWriter NetworkWriter; - public PooledNetworkReader NetworkReader; - public Stream NetworkBuffer; - public ArraySegment MessageData; - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs deleted file mode 100644 index a03dd19f35..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs +++ /dev/null @@ -1,818 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// MessageQueueContainer - /// Handles the management of a Message Queue - /// - internal class MessageQueueContainer : INetworkUpdateSystem, IDisposable - { - private const int k_MinQueueHistory = 2; //We need a minimum of 2 queue history buffers in order to properly handle looping back messages when a host - -#if UNITY_EDITOR || DEVELOPMENT_BUILD - private static int s_MessageQueueContainerInstances; -#endif - - public enum MessageType - { - ClientRpc, - ServerRpc, - UnnamedMessage, - NamedMessage, - SnapshotData, - SceneEvent, - - None //Indicates end of frame - } - - public enum MessageQueueProcessingTypes - { - Send, - Receive, - } - - private static readonly IReadOnlyDictionary k_MessageTypeNames; - - static MessageQueueContainer() - { - var messageTypeNames = new Dictionary(); - foreach (var messageType in Enum.GetValues(typeof(MessageType))) - { - messageTypeNames.Add((int)messageType, messageType.ToString()); - } - - k_MessageTypeNames = messageTypeNames; - } - - public static string GetMessageTypeName(MessageType messageType) - { - if (!k_MessageTypeNames.TryGetValue((int)messageType, out var messageTypeName)) - { - messageTypeName = string.Empty; - } - - return messageTypeName; - } - - // Inbound and Outbound QueueHistoryFrames - private readonly Dictionary>> m_QueueHistory = - new Dictionary>>(); - - private MessageQueueProcessor m_MessageQueueProcessor; - - private uint m_MaxFrameHistory; - private int m_InboundStreamBufferIndex; - private int m_OutBoundStreamBufferIndex; - private bool m_IsTestingEnabled; - private bool m_ProcessUpdateStagesExternally; - private bool m_IsNotUsingBatching; - - // TODO hack: Fixed update can run multiple times in a frame and the queue history frame doesn't get cleared - // until the end of the frame. This results in messages executing at FixedUpdate being invoked multiple times. - // This is used to prevent it being called more than once per frame. - // This will be fixed by the upcoming serialization refactor. - private bool m_HasRunFixedUpdate; - - internal readonly NetworkManager NetworkManager; - - /// - /// Returns if batching is enabled - /// - /// true or false - public bool IsUsingBatching() - { - return !m_IsNotUsingBatching; - } - - /// - /// Enables or disables batching - /// - /// true or false - public void EnableBatchedMessages(bool isbatchingEnabled) - { - m_IsNotUsingBatching = !isbatchingEnabled; - } - - /// - /// Creates a context for an internal command. - /// The context contains a NetworkWriter property used to fill out the command body. - /// If used as IDisposable, the command will be sent at the end of the using() block. - /// If not used as IDisposable, the command can be sent by calling context.Finalize() - /// - /// The type of message being sent - /// The channel the message is being sent on - /// The destinations for this message - /// The stage at which the message will be processed on the receiving side - /// - internal InternalCommandContext? EnterInternalCommandContext(MessageType messageType, NetworkDelivery networkDelivery, ulong[] clientIds, NetworkUpdateStage updateStage) - { - if (updateStage == NetworkUpdateStage.Initialization) - { - NetworkLog.LogWarning($"Trying to send a message of type {messageType} to be executed during Initialization stage. Changing to EarlyUpdate."); - updateStage = NetworkUpdateStage.EarlyUpdate; - } - - if (NetworkManager.IsServer) - { - clientIds = clientIds.Where(id => id != NetworkManager.ServerClientId).ToArray(); - } - - if (clientIds.Length == 0) - { - return null; - } - - var writer = BeginAddQueueItemToFrame(messageType, Time.realtimeSinceStartup, networkDelivery, NetworkManager.LocalClientId, clientIds, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - - writer.WriteByte((byte)messageType); - writer.WriteByte((byte)updateStage); // NetworkUpdateStage - - return new InternalCommandContext(writer, clientIds, updateStage, this); - } - - - /// - /// Will process the message queue and then move to the next available frame - /// - /// Inbound or Outbound - /// Network Update Stage assigned MessageQueueHistoryFrame to be processed and flushed - public void ProcessAndFlushMessageQueue(MessageQueueProcessingTypes queueType, NetworkUpdateStage currentUpdateStage) - { - bool isListening = !ReferenceEquals(NetworkManager, null) && NetworkManager.IsListening; - switch (queueType) - { - case MessageQueueProcessingTypes.Receive: - { - m_MessageQueueProcessor.ProcessReceiveQueue(currentUpdateStage, m_IsTestingEnabled); - break; - } - case MessageQueueProcessingTypes.Send: - { - m_MessageQueueProcessor.ProcessSendQueue(isListening); - break; - } - } - } - - /// - /// Gets the current frame for the Inbound or Outbound queue - /// - /// Inbound or Outbound - /// Network Update Stage the MessageQueueHistoryFrame is assigned to - /// QueueHistoryFrame - public MessageQueueHistoryFrame GetCurrentFrame(MessageQueueHistoryFrame.QueueFrameType qType, NetworkUpdateStage currentUpdateStage) - { - if (m_QueueHistory.ContainsKey(qType)) - { - int streamBufferIndex = GetStreamBufferIndex(qType); - - if (m_QueueHistory[qType].ContainsKey(streamBufferIndex)) - { - if (m_QueueHistory[qType][streamBufferIndex].ContainsKey(currentUpdateStage)) - { - return m_QueueHistory[qType][streamBufferIndex][currentUpdateStage]; - } - } - } - - return null; - } - - /// - /// Returns the queue type's current stream buffer index - /// - /// Inbound or Outbound - /// - private int GetStreamBufferIndex(MessageQueueHistoryFrame.QueueFrameType queueType) - { - return queueType == MessageQueueHistoryFrame.QueueFrameType.Inbound ? m_InboundStreamBufferIndex : m_OutBoundStreamBufferIndex; - } - - /// - /// Progresses the current frame to the next QueueHistoryFrame for the QueueHistoryFrame.QueueFrameType. - /// All other frames other than the current frame is considered the live rollback history - /// - /// Inbound or Outbound - public void AdvanceFrameHistory(MessageQueueHistoryFrame.QueueFrameType queueType) - { - int streamBufferIndex = GetStreamBufferIndex(queueType); - - if (!m_QueueHistory.ContainsKey(queueType)) - { - Debug.LogError($"You must initialize the {nameof(MessageQueueContainer)} before using Unity.Netcode!"); - return; - } - - if (!m_QueueHistory[queueType].ContainsKey(streamBufferIndex)) - { - Debug.LogError($"{nameof(MessageQueueContainer)} {queueType} queue stream buffer index out of range! [{streamBufferIndex}]"); - return; - } - - - foreach (KeyValuePair queueHistoryByUpdates in m_QueueHistory[queueType][streamBufferIndex]) - { - var messageQueueHistoryItem = queueHistoryByUpdates.Value; - - //This only gets reset when we advanced to next frame (do not reset this in the ResetQueueHistoryFrame) - messageQueueHistoryItem.HasLoopbackData = false; - - ResetQueueHistoryFrame(messageQueueHistoryItem); - IncrementAndSetQueueHistoryFrame(messageQueueHistoryItem); - } - - //Roll to the next stream buffer - streamBufferIndex++; - - //If we have hit our maximum history, roll back over to the first one - if (streamBufferIndex >= m_MaxFrameHistory) - { - streamBufferIndex = 0; - } - - if (queueType == MessageQueueHistoryFrame.QueueFrameType.Inbound) - { - m_InboundStreamBufferIndex = streamBufferIndex; - } - else - { - m_OutBoundStreamBufferIndex = streamBufferIndex; - } - } - - /// - /// IncrementAndSetQueueHistoryFrame - /// Increments and sets frame count for this queue frame - /// - /// QueueHistoryFrame to be reset - private void IncrementAndSetQueueHistoryFrame(MessageQueueHistoryFrame messageQueueFrame) - { - if (messageQueueFrame.GetQueueFrameType() == MessageQueueHistoryFrame.QueueFrameType.Inbound) - { - } - else - { - } - } - - /// - /// ResetQueueHistoryFrame - /// Resets the queue history frame passed to this method - /// - /// QueueHistoryFrame to be reset - private static void ResetQueueHistoryFrame(MessageQueueHistoryFrame messageQueueFrame) - { - //If we are dirt and have loopback data then don't clear this frame - if (messageQueueFrame.IsDirty && !messageQueueFrame.HasLoopbackData) - { - messageQueueFrame.TotalSize = 0; - messageQueueFrame.QueueItemOffsets.Clear(); - messageQueueFrame.QueueBuffer.Position = 0; - messageQueueFrame.MarkCurrentStreamPosition(); - messageQueueFrame.IsDirty = false; - } - } - - /// - /// AddQueueItemToInboundFrame - /// Adds a message queue item to the outbound frame - /// - /// type of message - /// when it was received - /// who sent the message - /// the message being received - internal void AddQueueItemToInboundFrame(MessageType messageType, float receiveTimestamp, ulong senderNetworkId, NetworkBuffer messageBuffer) - { - var updateStage = (NetworkUpdateStage)messageBuffer.ReadByte(); - - var messageFrameItem = GetQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Inbound, updateStage); - messageFrameItem.IsDirty = true; - - long startPosition = messageFrameItem.QueueBuffer.Position; - - //Write the packed version of the queueItem to our current queue history buffer - messageFrameItem.QueueWriter.WriteUInt16((ushort)messageType); - messageFrameItem.QueueWriter.WriteSingle(receiveTimestamp); - messageFrameItem.QueueWriter.WriteUInt64(senderNetworkId); - - //Inbound we copy the entire packet and store the position offset - long streamSize = messageBuffer.Length - messageBuffer.Position; - messageFrameItem.QueueWriter.WriteInt64(streamSize); - // This 0 is an offset into the following stream. Since we're copying from the offset rather than copying the whole buffer, it can stay at 0. - // In other words, we're not using the offset anymore, but it's being left for now in case it becomes necessary again later. - messageFrameItem.QueueWriter.WriteInt64(0); - messageFrameItem.QueueWriter.WriteBytes(messageBuffer.GetBuffer(), streamSize, (int)messageBuffer.Position); - - //Add the packed size to the offsets for parsing over various entries - messageFrameItem.QueueItemOffsets.Add((uint)messageFrameItem.QueueBuffer.Position); - - //Calculate the packed size based on stream progression - messageFrameItem.TotalSize += (uint)(messageFrameItem.QueueBuffer.Position - startPosition); - } - - /// - /// SetLoopBackFrameItem - /// ***Temporary fix for host mode loopback RPC writer work-around - /// Sets the next frame inbond buffer as the loopback queue history frame in the current frame's outbound buffer - /// - /// - public void SetLoopBackFrameItem(NetworkUpdateStage updateStage) - { - //Get the next frame's inbound queue history frame - var loopbackHistoryframe = GetQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Inbound, updateStage, true); - - //Get the current frame's outbound queue history frame - var messageQueueHistoryItem = GetQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - - if (messageQueueHistoryItem != null) - { - messageQueueHistoryItem.LoopbackHistoryFrame = loopbackHistoryframe; - } - else - { - Debug.LogError($"Could not find the outbound {nameof(MessageQueueHistoryFrame)}!"); - } - } - - /// - /// GetLoopBackWriter - /// Gets the loop back writer for the history frame (if one exists) - /// ***Temporary fix for host mode loopback RPC writer work-around - /// - /// type of queue frame - /// state it should be invoked on - /// - public MessageQueueHistoryFrame GetLoopBackHistoryFrame(MessageQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage) - { - return GetQueueHistoryFrame(queueFrameType, updateStage); - } - - /// - /// BeginAddQueueItemToOutboundFrame - /// Adds a queue item to the outbound queue frame - /// - /// type of the queue item - /// when queue item was submitted - /// channel this queue item is being sent - /// source network id of the sender - /// target network id(s) - /// type of queue frame - /// what update stage the RPC should be invoked on - /// PooledNetworkWriter - public PooledNetworkWriter BeginAddQueueItemToFrame(MessageType messageType, float sendTimestamp, NetworkDelivery networkDelivery, ulong senderNetworkId, ulong[] targetNetworkIds, - MessageQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage) - { - bool getNextFrame = NetworkManager.IsHost && queueFrameType == MessageQueueHistoryFrame.QueueFrameType.Inbound; - - var messageQueueHistoryFrame = GetQueueHistoryFrame(queueFrameType, updateStage, getNextFrame); - messageQueueHistoryFrame.IsDirty = true; - - //Write the packed version of the queueItem to our current queue history buffer - messageQueueHistoryFrame.QueueWriter.WriteUInt16((ushort)messageType); - messageQueueHistoryFrame.QueueWriter.WriteSingle(sendTimestamp); - messageQueueHistoryFrame.QueueWriter.WriteUInt64(senderNetworkId); - if (queueFrameType == MessageQueueHistoryFrame.QueueFrameType.Outbound) - { - messageQueueHistoryFrame.QueueWriter.WriteByte((byte)networkDelivery); - } - - if (queueFrameType != MessageQueueHistoryFrame.QueueFrameType.Inbound) - { - if (targetNetworkIds != null && targetNetworkIds.Length != 0) - { - //In the event the host is one of the networkIds, for outbound we want to ignore it (at this spot only!!) - //Get a count of clients we are going to send to (and write into the buffer) - var numberOfClients = 0; - for (int i = 0; i < targetNetworkIds.Length; i++) - { - if (NetworkManager.IsHost && targetNetworkIds[i] == NetworkManager.ServerClientId) - { - continue; - } - - numberOfClients++; - } - - //Write our total number of clients - messageQueueHistoryFrame.QueueWriter.WriteInt32(numberOfClients); - - //Now write the cliend ids - for (int i = 0; i < targetNetworkIds.Length; i++) - { - if (NetworkManager.IsHost && targetNetworkIds[i] == NetworkManager.ServerClientId) - { - continue; - } - - messageQueueHistoryFrame.QueueWriter.WriteUInt64(targetNetworkIds[i]); - } - } - else - { - messageQueueHistoryFrame.QueueWriter.WriteInt32(0); - } - } - - //Mark where we started in the stream to later determine the actual RPC message size (position before writing RPC message vs position after write has completed) - messageQueueHistoryFrame.MarkCurrentStreamPosition(); - - //Write a filler dummy size of 0 to hold this position in order to write to it once the RPC is done writing. - messageQueueHistoryFrame.QueueWriter.WriteInt64(0); - - if (NetworkManager.IsHost && queueFrameType == MessageQueueHistoryFrame.QueueFrameType.Inbound) - { - if (!IsUsingBatching()) - { - messageQueueHistoryFrame.QueueWriter.WriteInt64(1); - } - else - { - messageQueueHistoryFrame.QueueWriter.WriteInt64(0); - } - - messageQueueHistoryFrame.HasLoopbackData = true; //The only case for this is when it is the Host - } - - //Return the writer to the invoking method. - return messageQueueHistoryFrame.QueueWriter; - } - - /// - /// Signifies the end of this outbound RPC. - /// We store final MSG size and track the total current frame queue size - /// - /// NetworkWriter that was used - /// type of the queue frame that was used - /// stage the RPC is going to be invoked - public long EndAddQueueItemToFrame(NetworkWriter writer, MessageQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage) - { - bool getNextFrame = NetworkManager.IsHost && queueFrameType == MessageQueueHistoryFrame.QueueFrameType.Inbound; - - var messageQueueHistoryItem = GetQueueHistoryFrame(queueFrameType, updateStage, getNextFrame); - var loopBackHistoryFrame = messageQueueHistoryItem.LoopbackHistoryFrame; - - var pbWriter = (PooledNetworkWriter)writer; - if (pbWriter != messageQueueHistoryItem.QueueWriter) - { - Debug.LogError($"{nameof(MessageQueueContainer)} {queueFrameType} passed writer is not the same as the current {nameof(PooledNetworkWriter)} for the {queueFrameType}!"); - } - - //The total size of the frame is the last known position of the stream - messageQueueHistoryItem.TotalSize = (uint)messageQueueHistoryItem.QueueBuffer.Position; - - long currentPosition = messageQueueHistoryItem.QueueBuffer.Position; - ulong bitPosition = messageQueueHistoryItem.QueueBuffer.BitPosition; - - ////////////////////////////////////////////////////////////// - //>>>> REPOSITIONING STREAM TO RPC MESSAGE SIZE LOCATION <<<< - ////////////////////////////////////////////////////////////// - messageQueueHistoryItem.QueueBuffer.Position = messageQueueHistoryItem.GetCurrentMarkedPosition(); - - long messageOffset = 8; - if (getNextFrame && IsUsingBatching()) - { - messageOffset += 8; - } - - //subtracting 8 byte to account for the value of the size of the RPC (why the 8 above in - long messageSize = messageQueueHistoryItem.TotalSize - (messageQueueHistoryItem.GetCurrentMarkedPosition() + messageOffset); - - if (messageSize > 0) - { - //Write the actual size of the RPC message - messageQueueHistoryItem.QueueWriter.WriteInt64(messageSize); - } - else - { - Debug.LogWarning("MSGSize of < zero detected!! Setting message size to zero!"); - messageQueueHistoryItem.QueueWriter.WriteInt64(0); - } - - if (loopBackHistoryFrame != null) - { - if (messageSize > 0) - { - //Point to where the size of the message is stored - loopBackHistoryFrame.QueueBuffer.Position = loopBackHistoryFrame.GetCurrentMarkedPosition(); - - //Write the actual size of the RPC message - loopBackHistoryFrame.QueueWriter.WriteInt64(messageSize); - - if (!IsUsingBatching()) - { - //Write the offset for the header info copied - loopBackHistoryFrame.QueueWriter.WriteInt64(1); - } - else - { - //Write the offset for the header info copied - loopBackHistoryFrame.QueueWriter.WriteInt64(0); - } - - //Write message data - loopBackHistoryFrame.QueueWriter.WriteBytes( - messageQueueHistoryItem.QueueBuffer.GetBuffer(), messageSize - 2, - // Skip the 2 byte network header - // The network header is read on the receiving side to be able to call - // AddQueueItemToInboundFrame, which needs the message type and update stage - // (which are the two values in the network header) in order to create - // the inbound queue item. Here, we're skipping that - the loopback frame item - // is added to the inbound frame directly rather than passed along the wire. - // Since this skips the process that reads the network header, we skip writing it. - (int)messageQueueHistoryItem.QueueBuffer.Position + 2); - - //Set the total size for this stream - loopBackHistoryFrame.TotalSize = (uint)loopBackHistoryFrame.QueueBuffer.Position; - - //Add the total size to the offsets for parsing over various entries - loopBackHistoryFrame.QueueItemOffsets.Add((uint)loopBackHistoryFrame.QueueBuffer.Position); - } - else - { - Debug.LogWarning($"{nameof(messageSize)} < zero detected! Setting message size to zero!"); - //Write the actual size of the RPC message - loopBackHistoryFrame.QueueWriter.WriteInt64(0); - } - - messageQueueHistoryItem.LoopbackHistoryFrame = null; - } - - ////////////////////////////////////////////////////////////// - //<<<< REPOSITIONING STREAM BACK TO THE CURRENT TAIL >>>> - ////////////////////////////////////////////////////////////// - messageQueueHistoryItem.QueueBuffer.Position = currentPosition; - messageQueueHistoryItem.QueueBuffer.BitPosition = bitPosition; - - //Add the packed size to the offsets for parsing over various entries - messageQueueHistoryItem.QueueItemOffsets.Add((uint)messageQueueHistoryItem.QueueBuffer.Position); - - return messageSize; - } - - /// - /// Gets the current queue history frame (inbound or outbound) - /// - /// inbound or outbound - /// network update stage the queue history frame is assigned to - /// whether to get the next frame or not (true/false) - /// QueueHistoryFrame or null - public MessageQueueHistoryFrame GetQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType frameType, NetworkUpdateStage updateStage, bool getNextFrame = false) - { - int streamBufferIndex = GetStreamBufferIndex(frameType); - - //We want to write into the future/next frame - if (getNextFrame) - { - streamBufferIndex++; - - //If we have hit our maximum history, roll back over to the first one - if (streamBufferIndex >= m_MaxFrameHistory) - { - streamBufferIndex = 0; - } - } - - if (!m_QueueHistory.ContainsKey(frameType)) - { - Debug.LogError($"{nameof(MessageQueueHistoryFrame)} {nameof(MessageQueueHistoryFrame.QueueFrameType)} {frameType} does not exist!"); - return null; - } - - if (!m_QueueHistory[frameType].ContainsKey(streamBufferIndex)) - { - Debug.LogError($"{nameof(MessageQueueContainer)} {frameType} queue stream buffer index out of range! [{streamBufferIndex}]"); - return null; - } - - if (!m_QueueHistory[frameType][streamBufferIndex].ContainsKey(updateStage)) - { - Debug.LogError($"{nameof(MessageQueueContainer)} {updateStage} update type does not exist!"); - return null; - } - - return m_QueueHistory[frameType][streamBufferIndex][updateStage]; - } - - /// - /// The NetworkUpdate method used by the NetworkUpdateLoop - /// - /// the stage to process RPC Queues - public void NetworkUpdate(NetworkUpdateStage updateStage) - { - if (updateStage == NetworkUpdateStage.FixedUpdate) - { - if (m_HasRunFixedUpdate) - { - return; - } - - m_HasRunFixedUpdate = true; - } - ProcessAndFlushMessageQueue(MessageQueueProcessingTypes.Receive, updateStage); - - if (updateStage == NetworkUpdateStage.PostLateUpdate) - { - ProcessAndFlushMessageQueue(MessageQueueProcessingTypes.Send, updateStage); - m_MessageQueueProcessor.AdvanceFrameHistoryIfNeeded(); - m_HasRunFixedUpdate = false; - } - } - - /// - /// This will allocate [maxFrameHistory] + [1 currentFrame] number of PooledNetworkBuffers and keep them open until the session ends - /// Note: For zero frame history set maxFrameHistory to zero - /// - /// - private void Initialize(uint maxFrameHistory) - { - //This makes sure that we don't exceed a ridiculous value by capping the number of queue history frames to ushort.MaxValue - //If this value is exceeded, then it will be kept at the ceiling of ushort.Maxvalue. - //Note: If running at a 60pps rate (16ms update frequency) this would yield 17.47 minutes worth of queue frame history. - if (maxFrameHistory > ushort.MaxValue) - { - if (NetworkLog.CurrentLogLevel == LogLevel.Developer) - { - NetworkLog.LogWarning($"The {nameof(MessageQueueHistoryFrame)} size cannot exceed {ushort.MaxValue} {nameof(MessageQueueHistoryFrame)}s! Capping at {ushort.MaxValue} {nameof(MessageQueueHistoryFrame)}s."); - } - maxFrameHistory = ushort.MaxValue; - } - - ClearParameters(); - - m_MessageQueueProcessor = new MessageQueueProcessor(this, NetworkManager); - m_MaxFrameHistory = maxFrameHistory + k_MinQueueHistory; - - if (!m_QueueHistory.ContainsKey(MessageQueueHistoryFrame.QueueFrameType.Inbound)) - { - m_QueueHistory.Add(MessageQueueHistoryFrame.QueueFrameType.Inbound, new Dictionary>()); - } - - if (!m_QueueHistory.ContainsKey(MessageQueueHistoryFrame.QueueFrameType.Outbound)) - { - m_QueueHistory.Add(MessageQueueHistoryFrame.QueueFrameType.Outbound, new Dictionary>()); - } - - for (int i = 0; i < m_MaxFrameHistory; i++) - { - if (!m_QueueHistory[MessageQueueHistoryFrame.QueueFrameType.Outbound].ContainsKey(i)) - { - m_QueueHistory[MessageQueueHistoryFrame.QueueFrameType.Outbound].Add(i, new Dictionary()); - var queueHistoryFrame = new MessageQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - queueHistoryFrame.QueueBuffer = PooledNetworkBuffer.Get(); - queueHistoryFrame.QueueBuffer.Position = 0; - queueHistoryFrame.QueueWriter = PooledNetworkWriter.Get(queueHistoryFrame.QueueBuffer); - queueHistoryFrame.QueueReader = PooledNetworkReader.Get(queueHistoryFrame.QueueBuffer); - queueHistoryFrame.QueueItemOffsets = new List(); - - //For now all outbound, we will always have a single update in which they are processed (LATEUPDATE) - m_QueueHistory[MessageQueueHistoryFrame.QueueFrameType.Outbound][i].Add(NetworkUpdateStage.PostLateUpdate, queueHistoryFrame); - } - - if (!m_QueueHistory[MessageQueueHistoryFrame.QueueFrameType.Inbound].ContainsKey(i)) - { - m_QueueHistory[MessageQueueHistoryFrame.QueueFrameType.Inbound].Add(i, new Dictionary()); - - //For inbound, we create a queue history frame per update stage - foreach (NetworkUpdateStage netUpdateStage in Enum.GetValues(typeof(NetworkUpdateStage))) - { - var messageQueueHistoryFrame = new MessageQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Inbound, netUpdateStage); - messageQueueHistoryFrame.QueueBuffer = PooledNetworkBuffer.Get(); - messageQueueHistoryFrame.QueueBuffer.Position = 0; - messageQueueHistoryFrame.QueueWriter = PooledNetworkWriter.Get(messageQueueHistoryFrame.QueueBuffer); - messageQueueHistoryFrame.QueueReader = PooledNetworkReader.Get(messageQueueHistoryFrame.QueueBuffer); - messageQueueHistoryFrame.QueueItemOffsets = new List(); - m_QueueHistory[MessageQueueHistoryFrame.QueueFrameType.Inbound][i].Add(netUpdateStage, messageQueueHistoryFrame); - } - } - } - - //As long as this instance is using the pre-defined update stages - if (!m_ProcessUpdateStagesExternally) - { - //Register with the network update loop system - this.RegisterAllNetworkUpdates(); - } - } - - /// - /// Clears the stream indices and frames process properties - /// - private void ClearParameters() - { - m_InboundStreamBufferIndex = 0; - m_OutBoundStreamBufferIndex = 0; - } - - /// - /// Flushes the internal messages - /// Removes itself from the network update loop - /// Disposes readers, writers, clears the queue history, and resets any parameters - /// - private void Shutdown() - { -#if UNITY_EDITOR || DEVELOPMENT_BUILD - - if (NetworkLog.CurrentLogLevel == LogLevel.Developer) - { - NetworkLog.LogInfo($"[Instance : {s_MessageQueueContainerInstances}] {nameof(MessageQueueContainer)} shutting down."); - } -#endif - //As long as this instance is using the pre-defined update stages - if (!m_ProcessUpdateStagesExternally) - { - //Remove ourself from the network loop update system - this.UnregisterAllNetworkUpdates(); - } - - //Dispose of any readers and writers - foreach (var queueHistorySection in m_QueueHistory) - { - foreach (var queueHistoryItemByStage in queueHistorySection.Value) - { - foreach (var queueHistoryItem in queueHistoryItemByStage.Value) - { - queueHistoryItem.Value.QueueWriter?.Dispose(); - queueHistoryItem.Value.QueueReader?.Dispose(); - queueHistoryItem.Value.QueueBuffer?.Dispose(); - } - } - } - m_MessageQueueProcessor.Shutdown(); - - //Clear history and parameters - m_QueueHistory.Clear(); - - ClearParameters(); - } - - /// - /// Cleans up our instance count and warns if there instantiation issues - /// - public void Dispose() - { - Shutdown(); - -#if UNITY_EDITOR || DEVELOPMENT_BUILD - if (s_MessageQueueContainerInstances > 0) - { - if (NetworkLog.CurrentLogLevel == LogLevel.Developer) - { - NetworkLog.LogInfo($"[Instance : {s_MessageQueueContainerInstances}] {nameof(MessageQueueContainer)} disposed."); - } - - s_MessageQueueContainerInstances--; - } - else //This should never happen...if so something else has gone very wrong. - { - if (NetworkLog.CurrentLogLevel >= LogLevel.Normal) - { - NetworkLog.LogError($"[*** Warning ***] {nameof(MessageQueueContainer)} is being disposed twice?"); - } - - throw new Exception("[*** Warning ***] System state is not stable! Check all references to the Dispose method!"); - } -#endif - } - - /// - /// MessageQueueContainer - Constructor - /// Note about processExternally: this values determines if it will register with the Network Update Loop - /// or not. If not, then most likely unit tests are being preformed on this class. The default value is false. - /// - /// - /// determines if it handles processing externally - public MessageQueueContainer(NetworkManager networkManager, uint maxFrameHistory = 0, bool processExternally = false) - { - NetworkManager = networkManager; - -#if UNITY_EDITOR || DEVELOPMENT_BUILD - //Keep track of how many instances we have instantiated - s_MessageQueueContainerInstances++; - - if (NetworkLog.CurrentLogLevel == LogLevel.Developer) - { - NetworkLog.LogInfo($"[Instance : {s_MessageQueueContainerInstances}] {nameof(MessageQueueContainer)} Initialized"); - } -#endif - - m_ProcessUpdateStagesExternally = processExternally; - Initialize(maxFrameHistory); - } - - -#if UNITY_EDITOR || DEVELOPMENT_BUILD - /// - /// Enables testing of the MessageQueueContainer - /// - /// - public void SetTestingState(bool enabled) - { - m_IsTestingEnabled = enabled; - } -#endif - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs.meta deleted file mode 100644 index 940ca413f4..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a7afb55ac18f78a48b5a9af1766d6bfb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs deleted file mode 100644 index 20aa522487..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// Used by the MessageQueueContainer to hold queued messages - /// - internal class MessageQueueHistoryFrame - { - public enum QueueFrameType - { - Inbound, - Outbound, - } - - public bool IsDirty; //Used to determine if this queue history frame has been reset (cleaned) yet - public bool HasLoopbackData; //Used to determine if a dirt frame is dirty because messages are being looped back betwen HostClient and HostServer - public uint TotalSize; - public List QueueItemOffsets; - - public PooledNetworkBuffer QueueBuffer; - public PooledNetworkWriter QueueWriter; - public MessageQueueHistoryFrame LoopbackHistoryFrame; //Temporary fix for Host mode loopback work around. - - - public PooledNetworkReader QueueReader; - - private int m_QueueItemOffsetIndex; - private MessageFrameItem m_CurrentItem; - private readonly QueueFrameType m_QueueFrameType; - private int m_MaximumClients; - private long m_CurrentStreamSizeMark; - private NetworkUpdateStage m_StreamUpdateStage; //Update stage specific to messages (typically inbound has most potential for variation) - private int m_MaxStreamBounds; - private const int k_MinStreamBounds = 0; - - /// - /// Returns whether this is an inbound or outbound frame - /// - /// - public QueueFrameType GetQueueFrameType() - { - return m_QueueFrameType; - } - - /// - /// Marks the current size of the stream (used primarily for sanity checks) - /// - public void MarkCurrentStreamPosition() - { - if (QueueBuffer != null) - { - m_CurrentStreamSizeMark = QueueBuffer.Position; - } - else - { - m_CurrentStreamSizeMark = 0; - } - } - - /// - /// Returns the current position that was marked (to track size of msg) - /// - /// m_CurrentStreamSizeMark - public long GetCurrentMarkedPosition() - { - return m_CurrentStreamSizeMark; - } - - /// - /// Internal method to get the current Queue Item from the stream at its current position - /// - /// FrameQueueItem - internal MessageFrameItem GetCurrentQueueItem() - { - //Write the packed version of the queueItem to our current queue history buffer - m_CurrentItem.MessageType = (MessageQueueContainer.MessageType)QueueReader.ReadUInt16(); - m_CurrentItem.Timestamp = QueueReader.ReadSingle(); - m_CurrentItem.NetworkId = QueueReader.ReadUInt64(); - if (m_QueueFrameType == QueueFrameType.Outbound) - { - m_CurrentItem.Delivery = (NetworkDelivery)QueueReader.ReadByteDirect(); - } - - //Clear out any current value for the client ids - m_CurrentItem.ClientNetworkIds = new ulong[0]; - - //If outbound, determine if any client ids needs to be added - if (m_QueueFrameType == QueueFrameType.Outbound) - { - //Outbound we care about both channel and clients - int numClients = QueueReader.ReadInt32(); - if (numClients > 0 && numClients < m_MaximumClients) - { - ulong[] clientIdArray = new ulong[numClients]; - for (int i = 0; i < numClients; i++) - { - clientIdArray[i] = QueueReader.ReadUInt64(); - } - - m_CurrentItem.ClientNetworkIds = clientIdArray; - } - } - - m_CurrentItem.UpdateStage = m_StreamUpdateStage; - - //Get the stream size - m_CurrentItem.StreamSize = QueueReader.ReadInt64(); - - //Sanity checking for boundaries - if (m_CurrentItem.StreamSize < m_MaxStreamBounds && m_CurrentItem.StreamSize >= k_MinStreamBounds) - { - //Inbound and Outbound message streams are handled differently - if (m_QueueFrameType == QueueFrameType.Inbound) - { - //Get our offset - long position = QueueReader.ReadInt64(); - - //Always make sure we are positioned at the start of the stream before we write - m_CurrentItem.NetworkBuffer.Position = 0; - - //Write the entire message to the m_CurrentQueueItem stream (1 stream is re-used for all incoming messages) - m_CurrentItem.NetworkWriter.ReadAndWrite(QueueReader, m_CurrentItem.StreamSize); - - //Reset the position back to the offset so std API can process the message properly - //(i.e. minus the already processed header) - m_CurrentItem.NetworkBuffer.Position = position; - } - else - { - //Create a byte array segment for outbound sending - m_CurrentItem.MessageData = QueueReader.CreateArraySegment((int)m_CurrentItem.StreamSize, (int)QueueBuffer.Position); - } - } - else - { - Debug.LogWarning($"{nameof(m_CurrentItem)}.{nameof(MessageFrameItem.StreamSize)} exceeds allowed size ({m_MaxStreamBounds} vs {m_CurrentItem.StreamSize})! Exiting from the current MessageQueue enumeration loop!"); - m_CurrentItem.MessageType = MessageQueueContainer.MessageType.None; - } - - return m_CurrentItem; - } - - /// - /// Handles getting the next queue item from this frame - /// If none are remaining, then it returns a queue item type of NONE - /// - /// FrameQueueItem - internal MessageFrameItem GetNextQueueItem() - { - QueueBuffer.Position = QueueItemOffsets[m_QueueItemOffsetIndex]; - m_QueueItemOffsetIndex++; - if (m_QueueItemOffsetIndex >= QueueItemOffsets.Count) - { - m_CurrentItem.MessageType = MessageQueueContainer.MessageType.None; - return m_CurrentItem; - } - - return GetCurrentQueueItem(); - } - - /// - /// Should be called the first time a queue item is pulled from a queue history frame. - /// This will reset the frame's stream indices and add a new stream and stream writer to the m_CurrentQueueItem instance. - /// - /// FrameQueueItem - internal MessageFrameItem GetFirstQueueItem() - { - if (QueueBuffer.Position > 0) - { - m_QueueItemOffsetIndex = 0; - QueueBuffer.Position = 0; - - if (m_QueueFrameType == QueueFrameType.Inbound) - { - if (m_CurrentItem.NetworkBuffer == null) - { - m_CurrentItem.NetworkBuffer = PooledNetworkBuffer.Get(); - } - - if (m_CurrentItem.NetworkWriter == null) - { - m_CurrentItem.NetworkWriter = PooledNetworkWriter.Get(m_CurrentItem.NetworkBuffer); - } - - if (m_CurrentItem.NetworkReader == null) - { - m_CurrentItem.NetworkReader = PooledNetworkReader.Get(m_CurrentItem.NetworkBuffer); - } - } - - return GetCurrentQueueItem(); - } - - m_CurrentItem.MessageType = MessageQueueContainer.MessageType.None; - return m_CurrentItem; - } - - /// - /// Should be called once all processing of the current frame is complete. - /// This only closes the m_CurrentQueueItem's stream which is used as a "middle-man" (currently) - /// for delivering the message to the method requesting a queue item from a frame. - /// - public void CloseQueue() - { - if (m_CurrentItem.NetworkWriter != null) - { - m_CurrentItem.NetworkWriter.Dispose(); - m_CurrentItem.NetworkWriter = null; - } - - if (m_CurrentItem.NetworkReader != null) - { - m_CurrentItem.NetworkReader.Dispose(); - m_CurrentItem.NetworkReader = null; - } - - if (m_CurrentItem.NetworkBuffer != null) - { - m_CurrentItem.NetworkBuffer.Dispose(); - m_CurrentItem.NetworkBuffer = null; - } - } - - - /// - /// QueueHistoryFrame Constructor - /// - /// Inbound or Outbound - /// Network Update Stage this MessageQueueHistoryFrame is assigned to - /// maximum number of clients - /// maximum size of the message stream a message can have (defaults to 1MB) - public MessageQueueHistoryFrame(QueueFrameType queueType, NetworkUpdateStage updateStage, int maxClients = 512, int maxStreamBounds = 1 << 20) - { - //The added 512 is the Queue History Frame header information, leaving room to grow - m_MaxStreamBounds = maxStreamBounds + 512; - m_MaximumClients = maxClients; - m_QueueFrameType = queueType; - m_CurrentItem = new MessageFrameItem(); - m_StreamUpdateStage = updateStage; - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs.meta deleted file mode 100644 index f358c46a3f..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueHistoryFrame.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4a459a55be3842e45a39f5e6129dbd21 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs deleted file mode 100644 index 16be7876f2..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs +++ /dev/null @@ -1,279 +0,0 @@ -using System; -using System.Collections.Generic; -using Unity.Profiling; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// MessageQueueProcessing - /// Handles processing of MessageQueues - /// Inbound to invocation - /// Outbound to send - /// - internal class MessageQueueProcessor - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - private static ProfilerMarker s_ProcessReceiveQueue = new ProfilerMarker($"{nameof(MessageQueueProcessor)}.{nameof(ProcessReceiveQueue)}"); - private static ProfilerMarker s_ProcessSendQueue = new ProfilerMarker($"{nameof(MessageQueueProcessor)}.{nameof(ProcessSendQueue)}"); -#endif - - // Batcher object used to manage the message batching on the send side - private readonly MessageBatcher m_MessageBatcher = new MessageBatcher(); - private const int k_BatchThreshold = 512; - // Selected mostly arbitrarily... Better solution to come soon. - private const int k_FragmentationThreshold = 1024; - - private bool m_AdvanceInboundFrameHistory = false; - - //The MessageQueueContainer that is associated with this MessageQueueProcessor - private MessageQueueContainer m_MessageQueueContainer; - - private readonly NetworkManager m_NetworkManager; - private readonly List m_TargetIdBuffer = new List(); - - public void Shutdown() - { - m_MessageBatcher.Shutdown(); - } - - public void ProcessMessage(in MessageFrameItem item) - { - try - { - switch (item.MessageType) - { - case MessageQueueContainer.MessageType.ClientRpc: - case MessageQueueContainer.MessageType.ServerRpc: - // Can rely on currentStage == the original updateStage in the buffer - // After all, that's the whole point of it being in the buffer. - m_NetworkManager.InvokeRpc(item, item.UpdateStage); - break; - case MessageQueueContainer.MessageType.UnnamedMessage: - m_NetworkManager.MessageHandler.HandleUnnamedMessage(item.NetworkId, item.NetworkBuffer); - break; - case MessageQueueContainer.MessageType.NamedMessage: - m_NetworkManager.MessageHandler.HandleNamedMessage(item.NetworkId, item.NetworkBuffer); - break; - case MessageQueueContainer.MessageType.SceneEvent: - m_NetworkManager.MessageHandler.HandleSceneEvent(item.NetworkId, item.NetworkBuffer); - break; - - default: - NetworkLog.LogWarning($"Received unknown message {((int)item.MessageType).ToString()}"); - break; - } - } - catch (Exception ex) - { - Debug.LogException(ex); - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"A {item.MessageType} threw an exception while executing! Please check Unity logs for more information."); - } - } - } - - /// - /// ProcessReceiveQueue - /// Public facing interface method to start processing all messages in the current inbound frame - /// - public void ProcessReceiveQueue(NetworkUpdateStage currentStage, bool isTesting) - { - if (!ReferenceEquals(m_MessageQueueContainer, null)) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - s_ProcessReceiveQueue.Begin(); -#endif - var currentFrame = m_MessageQueueContainer.GetQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Inbound, currentStage); - var nextFrame = m_MessageQueueContainer.GetQueueHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Inbound, currentStage, true); - if (nextFrame.IsDirty && nextFrame.HasLoopbackData) - { - m_AdvanceInboundFrameHistory = true; - } - - if (currentFrame != null && currentFrame.IsDirty) - { - var currentQueueItem = currentFrame.GetFirstQueueItem(); - while (currentQueueItem.MessageType != MessageQueueContainer.MessageType.None) - { - m_AdvanceInboundFrameHistory = true; - - if (!isTesting) - { - currentQueueItem.UpdateStage = currentStage; - ProcessMessage(currentQueueItem); - } - - currentQueueItem = currentFrame.GetNextQueueItem(); - } - - //We call this to dispose of the shared stream writer and stream - currentFrame.CloseQueue(); - } - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - s_ProcessReceiveQueue.End(); -#endif - } - } - - public void AdvanceFrameHistoryIfNeeded() - { - if (m_AdvanceInboundFrameHistory) - { - m_MessageQueueContainer.AdvanceFrameHistory(MessageQueueHistoryFrame.QueueFrameType.Inbound); - m_AdvanceInboundFrameHistory = false; - } - } - - /// - /// ProcessSendQueue - /// Called to send both performance RPC and internal messages and then flush the outbound queue - /// - internal void ProcessSendQueue(bool isListening) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - s_ProcessSendQueue.Begin(); -#endif - - MessageQueueSendAndFlush(isListening); - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - s_ProcessSendQueue.End(); -#endif - } - - /// - /// FillTargetList - /// Fills a list with the ClientId's an item is targeted to - /// - /// the MessageQueueItem we want targets for - /// the list to fill - private static void FillTargetList(in MessageFrameItem item, List targetList) - { - switch (item.MessageType) - { - case MessageQueueContainer.MessageType.ServerRpc: - targetList.Add(item.NetworkId); - break; - default: - // todo: consider the implications of default usage of queueItem.clientIds - case MessageQueueContainer.MessageType.ClientRpc: - // copy the list - targetList.AddRange(item.ClientNetworkIds); - break; - } - } - - /// - /// Sends all message queue items in the current outbound frame - /// - /// if flase it will just process through the queue items but attempt to send - private void MessageQueueSendAndFlush(bool isListening) - { - var advanceFrameHistory = false; - if (!ReferenceEquals(m_MessageQueueContainer, null)) - { - var currentFrame = m_MessageQueueContainer.GetCurrentFrame(MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - if (currentFrame != null) - { - var currentQueueItem = currentFrame.GetFirstQueueItem(); - while (currentQueueItem.MessageType != MessageQueueContainer.MessageType.None) - { - advanceFrameHistory = true; - if (isListening) - { - m_TargetIdBuffer.Clear(); - FillTargetList(currentQueueItem, m_TargetIdBuffer); - - if (m_MessageQueueContainer.IsUsingBatching()) - { - m_MessageBatcher.QueueItem(m_TargetIdBuffer, currentQueueItem, k_BatchThreshold, SendCallback); - } - else - { - SendFrameQueueItem(m_TargetIdBuffer, currentQueueItem); - } - - foreach (var target in m_TargetIdBuffer) - { - m_NetworkManager.NetworkMetrics.TrackNetworkMessageSent(target, MessageQueueContainer.GetMessageTypeName(currentQueueItem.MessageType), currentQueueItem.MessageData.Count); - } - } - - currentQueueItem = currentFrame.GetNextQueueItem(); - } - - //If the size is < m_BatchThreshold then just send the messages - if (isListening && advanceFrameHistory && m_MessageQueueContainer.IsUsingBatching()) - { - m_MessageBatcher.SendItems(0, SendCallback); - } - } - - //If we processed any messages, then advance to the next frame - if (advanceFrameHistory) - { - m_MessageQueueContainer.AdvanceFrameHistory(MessageQueueHistoryFrame.QueueFrameType.Outbound); - } - } - } - - /// - /// This is the callback from the batcher when it need to send a batch - /// - /// clientId to send to - /// the stream to send - private void SendCallback(ulong clientId, MessageBatcher.SendStream sendStream) - { - var length = (int)sendStream.Buffer.Length; - var bytes = sendStream.Buffer.GetBuffer(); - var sendBuffer = new ArraySegment(bytes, 0, length); - - var networkDelivery = sendStream.Delivery; - // If the length is greater than the fragmented threshold, switch to a fragmented channel. - // This is kind of a hack to get around issues with certain usages patterns on fragmentation with UNet. - // We send everything unfragmented to avoid those issues, and only switch to the fragmented channel - // if we have no other choice. - if (length > k_FragmentationThreshold) - { - networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; - } - - m_MessageQueueContainer.NetworkManager.NetworkMetrics.TrackTransportBytesSent(length); - m_MessageQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(clientId, sendBuffer, networkDelivery); - } - - /// - /// SendFrameQueueItem - /// Sends the Message Queue Item to the specified destination - /// - /// Information on what to send - private void SendFrameQueueItem(IReadOnlyCollection targetIds, in MessageFrameItem item) - { - var networkDelivery = item.Delivery; - // If the length is greater than the fragmented threshold, switch to a fragmented channel. - // This is kind of a hack to get around issues with certain usages patterns on fragmentation with UNet. - // We send everything unfragmented to avoid those issues, and only switch to the fragmented channel - // if we have no other choice. - if (item.MessageData.Count > k_FragmentationThreshold) - { - networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; - } - - foreach (var clientId in targetIds) - { - m_MessageQueueContainer.NetworkManager.NetworkMetrics.TrackTransportBytesSent(item.MessageData.Count); - m_MessageQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(clientId, item.MessageData, networkDelivery); - } - } - - internal MessageQueueProcessor(MessageQueueContainer messageQueueContainer, NetworkManager networkManager) - { - m_MessageQueueContainer = messageQueueContainer; - m_NetworkManager = networkManager; - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs.meta deleted file mode 100644 index 31916fc881..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d81f244f7054e46469f5a3e8a7327024 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs new file mode 100644 index 0000000000..4d35dc8197 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs @@ -0,0 +1,22 @@ +namespace Unity.Netcode.Messages +{ + internal struct NamedMessage: INetworkMessage + { + public ulong Hash; + public FastBufferWriter Data; + + public unsafe void Serialize(ref FastBufferWriter writer) + { + writer.WriteValueSafe(Hash); + writer.WriteBytesSafe(Data.GetUnsafePtr(), Data.Length); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var message = new NamedMessage(); + reader.ReadValueSafe(out message.Hash); + + ((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(message.Hash, context.SenderId, ref reader); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs.meta index 69b0cf5836..f3977c4080 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1b0dde7481361454ab22f5fca90c4c24 +guid: 2b6bcd41dcce65743ba387fc4e2c6fa8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs new file mode 100644 index 0000000000..30abc04bc2 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs @@ -0,0 +1,102 @@ +using System; +using System.Transactions; + +namespace Unity.Netcode.Messages +{ + internal struct RpcMessage : INetworkMessage + { + public enum RpcType : byte + { + Server, + Client + } + + public struct Metadata + { + public RpcType Type; + public ulong NetworkObjectId; + public ushort NetworkBehaviourId; + public uint NetworkMethodId; + } + + public Metadata Data; + public FastBufferWriter RPCData; + + + public unsafe void Serialize(ref FastBufferWriter writer) + { + if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Data) + RPCData.Length)) + { + throw new OverflowException("Not enough space in the buffer to store RPC data."); + } + writer.WriteValue(Data); + writer.WriteBytes(RPCData.GetUnsafePtr(), RPCData.Length); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + var message = new RpcMessage(); + if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.Data))) + { + throw new OverflowException("Not enough space in the buffer to read RPC data."); + } + reader.ReadValue(out message.Data); + message.Handle(ref reader, (NetworkManager) context.SystemOwner, context.SenderId); + } + + public void Handle(ref FastBufferReader reader, NetworkManager networkManager, ulong senderId) + { + if (NetworkManager.__rpc_func_table.ContainsKey(Data.NetworkMethodId)) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(Data.NetworkObjectId)) + { + return; + } + + var networkObject = networkManager.SpawnManager.SpawnedObjects[Data.NetworkObjectId]; + + var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(Data.NetworkBehaviourId); + if (networkBehaviour == null) + { + return; + } + + var rpcParams = new __RpcParams(); + switch (Data.Type) + { + case RpcType.Server: + rpcParams.Server = new ServerRpcParams + { + Receive = new ServerRpcReceiveParams + { + SenderClientId = senderId + } + }; + break; + case RpcType.Client: + rpcParams.Client = new ClientRpcParams + { + Receive = new ClientRpcReceiveParams + { + } + }; + break; + } + + NetworkManager.__rpc_func_table[Data.NetworkMethodId](networkBehaviour, ref reader, rpcParams); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (NetworkManager.__rpc_name_table.TryGetValue(Data.NetworkMethodId, out var rpcMethodName)) + { + networkManager.NetworkMetrics.TrackRpcReceived( + senderId, + Data.NetworkObjectId, + rpcMethodName, + networkBehaviour.__getTypeName(), + reader.Length); + } +#endif + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs.meta index 7ce284cfef..e0a661e791 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d17c5017b93fd444ba31f8824025957c +guid: f8fc9f8cca6a18b428460b62bce2d8f7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs new file mode 100644 index 0000000000..c067ac19c4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs @@ -0,0 +1,19 @@ +namespace Unity.Netcode.Messages +{ + // Todo: Would be lovely to get this one nicely formatted with all the data it sends in the struct + // like most of the other messages when we have some more time and can come back and refactor this. + internal struct SceneEventMessage : INetworkMessage + { + public SceneEventData EventData; + + public void Serialize(ref FastBufferWriter writer) + { + EventData.Serialize(ref writer); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + ((NetworkManager)context.SystemOwner).SceneManager.HandleSceneEvent(context.SenderId, ref reader); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs.meta index c23ed27901..9e4e619fcd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageFrameItem.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dd8d54fea7e319940b47c30a4aadff15 +guid: 152f6ccef0320cf4abcf19099c1997e3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs new file mode 100644 index 0000000000..d995b020b4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs @@ -0,0 +1,17 @@ +namespace Unity.Netcode.Messages +{ + internal struct UnnamedMessage: INetworkMessage + { + public FastBufferWriter Data; + + public unsafe void Serialize(ref FastBufferWriter writer) + { + writer.WriteBytesSafe(Data.GetUnsafePtr(), Data.Length); + } + + public static void Receive(ref FastBufferReader reader, NetworkContext context) + { + ((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeUnnamedMessage(context.SenderId, ref reader); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs.meta index 0f738540a2..838f2b445e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/InternalCommandContext.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d855a86964512f147b1766c70f027504 +guid: b7cece0a7c7653648a7bc8fa920843be MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs index ae81a523bb..33e69bb852 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs @@ -1,34 +1,13 @@ +using Unity.Collections; + namespace Unity.Netcode { - public interface IHasUpdateStage + public struct ServerRpcSendParams { - NetworkUpdateStage UpdateStage - { - get; - set; - } - } - - public struct ServerRpcSendParams : IHasUpdateStage - { - private NetworkUpdateStage m_UpdateStage; - - public NetworkUpdateStage UpdateStage - { - get => m_UpdateStage; - set => m_UpdateStage = value; - } } - public struct ServerRpcReceiveParams : IHasUpdateStage + public struct ServerRpcReceiveParams { - private NetworkUpdateStage m_UpdateStage; - - public NetworkUpdateStage UpdateStage - { - get => m_UpdateStage; - set => m_UpdateStage = value; - } public ulong SenderClientId; } @@ -38,27 +17,22 @@ public struct ServerRpcParams public ServerRpcReceiveParams Receive; } - public struct ClientRpcSendParams : IHasUpdateStage + public struct ClientRpcSendParams { - private NetworkUpdateStage m_UpdateStage; - - public NetworkUpdateStage UpdateStage - { - get => m_UpdateStage; - set => m_UpdateStage = value; - } + /// + /// ulong array version of target id list - use either this OR TargetClientIdsNativeArray + /// public ulong[] TargetClientIds; + + /// + /// NativeArray version of target id list - use either this OR TargetClientIds + /// This option avoids any GC allocations but is a bit trickier to use. + /// + public NativeArray? TargetClientIdsNativeArray; } - public struct ClientRpcReceiveParams : IHasUpdateStage + public struct ClientRpcReceiveParams { - private NetworkUpdateStage m_UpdateStage; - - public NetworkUpdateStage UpdateStage - { - get => m_UpdateStage; - set => m_UpdateStage = value; - } } public struct ClientRpcParams diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs deleted file mode 100644 index f0a0d69f32..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace Unity.Netcode -{ - internal struct BufferSizeCapture - { - private readonly long m_InitialLength; - private readonly NetworkBuffer m_Buffer; - - private long m_PreviousLength; - - public BufferSizeCapture(NetworkBuffer buffer) - { - m_Buffer = buffer; - m_InitialLength = buffer.Length; - m_PreviousLength = m_InitialLength; - } - - public long Flush() - { - var currentLength = m_Buffer.Length; - var segmentLength = currentLength - m_PreviousLength + m_InitialLength; - - m_PreviousLength = currentLength; - - return segmentLength; - } - } - - internal struct CommandContextSizeCapture - { - private readonly InternalCommandContext m_Context; - private long m_OverheadBegin; - private long m_OverheadEnd; - private long m_SegmentBegin; - - public CommandContextSizeCapture(InternalCommandContext context) - { - m_Context = context; - m_OverheadBegin = 0L; - m_OverheadEnd = 0L; - m_SegmentBegin = 0L; - } - - public void StartMeasureOverhead() - { - m_OverheadBegin = m_Context.NetworkWriter.GetStream().Position; - } - - public void StopMeasureOverhead() - { - m_OverheadEnd = m_Context.NetworkWriter.GetStream().Position; - } - - public void StartMeasureSegment() - { - m_SegmentBegin = m_Context.NetworkWriter.GetStream().Position; - } - - public long StopMeasureSegment() - { - var segmentEnd = m_Context.NetworkWriter.GetStream().Position; - - return m_OverheadEnd - m_OverheadBegin + segmentEnd - m_SegmentBegin; - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs.meta b/com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs.meta deleted file mode 100644 index d4697c6c67..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/BufferSizeCapture.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 669bf3c074851d748bc8bf5ce4b78768 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs index 336d4d730c..aa1e7c3c5e 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs @@ -62,166 +62,6 @@ public override void ResetDirty() m_DirtyEvents.Clear(); } - /// - public override void ReadDelta(Stream stream, bool keepDirtyDelta) - { - using var reader = PooledNetworkReader.Get(stream); - ushort deltaCount = reader.ReadUInt16Packed(); - for (int i = 0; i < deltaCount; i++) - { - var eventType = (NetworkDictionaryEvent.EventType)reader.ReadBits(3); - switch (eventType) - { - case NetworkDictionaryEvent.EventType.Add: - { - var key = (TKey)reader.ReadObjectPacked(typeof(TKey)); - var value = (TValue)reader.ReadObjectPacked(typeof(TValue)); - m_Dictionary.Add(key, value); - - if (OnDictionaryChanged != null) - { - OnDictionaryChanged(new NetworkDictionaryEvent - { - Type = eventType, - Key = key, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkDictionaryEvent() - { - Type = eventType, - Key = key, - Value = value - }); - } - } - break; - case NetworkDictionaryEvent.EventType.Remove: - { - var key = (TKey)reader.ReadObjectPacked(typeof(TKey)); - TValue value; - m_Dictionary.TryGetValue(key, out value); - m_Dictionary.Remove(key); - - if (OnDictionaryChanged != null) - { - OnDictionaryChanged(new NetworkDictionaryEvent - { - Type = eventType, - Key = key, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkDictionaryEvent() - { - Type = eventType, - Key = key, - Value = value - }); - } - } - break; - case NetworkDictionaryEvent.EventType.RemovePair: - { - var key = (TKey)reader.ReadObjectPacked(typeof(TKey)); - var value = (TValue)reader.ReadObjectPacked(typeof(TValue)); - m_Dictionary.Remove(new KeyValuePair(key, value)); - - if (OnDictionaryChanged != null) - { - OnDictionaryChanged(new NetworkDictionaryEvent - { - Type = eventType, - Key = key, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkDictionaryEvent() - { - Type = eventType, - Key = key, - Value = value - }); - } - } - break; - case NetworkDictionaryEvent.EventType.Clear: - { - //read nothing - m_Dictionary.Clear(); - - if (OnDictionaryChanged != null) - { - OnDictionaryChanged(new NetworkDictionaryEvent - { - Type = eventType - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkDictionaryEvent - { - Type = eventType - }); - } - } - break; - case NetworkDictionaryEvent.EventType.Value: - { - var key = (TKey)reader.ReadObjectPacked(typeof(TKey)); - var value = (TValue)reader.ReadObjectPacked(typeof(TValue)); - - m_Dictionary[key] = value; - - if (OnDictionaryChanged != null) - { - OnDictionaryChanged(new NetworkDictionaryEvent - { - Type = eventType, - Key = key, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkDictionaryEvent() - { - Type = eventType, - Key = key, - Value = value - }); - } - } - break; - } - } - } - - /// - public override void ReadField(Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - m_Dictionary.Clear(); - ushort entryCount = reader.ReadUInt16Packed(); - for (int i = 0; i < entryCount; i++) - { - var key = (TKey)reader.ReadObjectPacked(typeof(TKey)); - var value = (TValue)reader.ReadObjectPacked(typeof(TValue)); - m_Dictionary.Add(key, value); - } - } - /// public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { @@ -386,60 +226,6 @@ public bool TryGetValue(TKey key, out TValue value) return m_Dictionary.TryGetValue(key, out value); } - /// - public override void WriteDelta(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteUInt16Packed((ushort)m_DirtyEvents.Count); - for (int i = 0; i < m_DirtyEvents.Count; i++) - { - writer.WriteBits((byte)m_DirtyEvents[i].Type, 3); - switch (m_DirtyEvents[i].Type) - { - case NetworkDictionaryEvent.EventType.Add: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Key); - writer.WriteObjectPacked(m_DirtyEvents[i].Value); - } - break; - case NetworkDictionaryEvent.EventType.Remove: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Key); - } - break; - case NetworkDictionaryEvent.EventType.RemovePair: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Key); - writer.WriteObjectPacked(m_DirtyEvents[i].Value); - } - break; - case NetworkDictionaryEvent.EventType.Clear: - { - //write nothing - } - break; - case NetworkDictionaryEvent.EventType.Value: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Key); - writer.WriteObjectPacked(m_DirtyEvents[i].Value); - } - break; - } - } - } - - /// - public override void WriteField(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteUInt16Packed((ushort)m_Dictionary.Count); - foreach (KeyValuePair pair in m_Dictionary) - { - writer.WriteObjectPacked(pair.Key); - writer.WriteObjectPacked(pair.Value); - } - } - /// public override void WriteDelta(ref FastBufferWriter writer) { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index b369ca4deb..5d9c2ae72f 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -68,63 +68,6 @@ public override bool IsDirty() return base.IsDirty() || m_DirtyEvents.Count > 0; } - /// - public override void WriteDelta(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteUInt16Packed((ushort)m_DirtyEvents.Count); - for (int i = 0; i < m_DirtyEvents.Count; i++) - { - writer.WriteBits((byte)m_DirtyEvents[i].Type, 3); - switch (m_DirtyEvents[i].Type) - { - case NetworkListEvent.EventType.Add: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Value); //BOX - } - break; - case NetworkListEvent.EventType.Insert: - { - writer.WriteInt32Packed(m_DirtyEvents[i].Index); - writer.WriteObjectPacked(m_DirtyEvents[i].Value); //BOX - } - break; - case NetworkListEvent.EventType.Remove: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Value); //BOX - } - break; - case NetworkListEvent.EventType.RemoveAt: - { - writer.WriteInt32Packed(m_DirtyEvents[i].Index); - } - break; - case NetworkListEvent.EventType.Value: - { - writer.WriteInt32Packed(m_DirtyEvents[i].Index); - writer.WriteObjectPacked(m_DirtyEvents[i].Value); //BOX - } - break; - case NetworkListEvent.EventType.Clear: - { - //Nothing has to be written - } - break; - } - } - } - - /// - public override void WriteField(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteUInt16Packed((ushort)m_List.Count); - for (int i = 0; i < m_List.Count; i++) - { - writer.WriteObjectPacked(m_List[i]); //BOX - } - } - /// public override void WriteDelta(ref FastBufferWriter writer) { @@ -180,190 +123,6 @@ public override void WriteField(ref FastBufferWriter writer) } } - /// - public override void ReadField(Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - m_List.Clear(); - ushort count = reader.ReadUInt16Packed(); - for (int i = 0; i < count; i++) - { - m_List.Add((T)reader.ReadObjectPacked(typeof(T))); //BOX - } - } - - /// - public override void ReadDelta(Stream stream, bool keepDirtyDelta) - { - using var reader = PooledNetworkReader.Get(stream); - ushort deltaCount = reader.ReadUInt16Packed(); - for (int i = 0; i < deltaCount; i++) - { - var eventType = (NetworkListEvent.EventType)reader.ReadBits(3); - switch (eventType) - { - case NetworkListEvent.EventType.Add: - { - m_List.Add((T)reader.ReadObjectPacked(typeof(T))); //BOX - - if (OnListChanged != null) - { - OnListChanged(new NetworkListEvent - { - Type = eventType, - Index = m_List.Count - 1, - Value = m_List[m_List.Count - 1] - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkListEvent() - { - Type = eventType, - Index = m_List.Count - 1, - Value = m_List[m_List.Count - 1] - }); - } - } - break; - case NetworkListEvent.EventType.Insert: - { - int index = reader.ReadInt32Packed(); - m_List.Insert(index, (T)reader.ReadObjectPacked(typeof(T))); //BOX - - if (OnListChanged != null) - { - OnListChanged(new NetworkListEvent - { - Type = eventType, - Index = index, - Value = m_List[index] - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkListEvent() - { - Type = eventType, - Index = index, - Value = m_List[index] - }); - } - } - break; - case NetworkListEvent.EventType.Remove: - { - var value = (T)reader.ReadObjectPacked(typeof(T)); //BOX - int index = m_List.IndexOf(value); - m_List.RemoveAt(index); - - if (OnListChanged != null) - { - OnListChanged(new NetworkListEvent - { - Type = eventType, - Index = index, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkListEvent() - { - Type = eventType, - Index = index, - Value = value - }); - } - } - break; - case NetworkListEvent.EventType.RemoveAt: - { - int index = reader.ReadInt32Packed(); - T value = m_List[index]; - m_List.RemoveAt(index); - - if (OnListChanged != null) - { - OnListChanged(new NetworkListEvent - { - Type = eventType, - Index = index, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkListEvent() - { - Type = eventType, - Index = index, - Value = value - }); - } - } - break; - case NetworkListEvent.EventType.Value: - { - int index = reader.ReadInt32Packed(); - var value = (T)reader.ReadObjectPacked(typeof(T)); //BOX - if (index < m_List.Count) - { - m_List[index] = value; - } - - if (OnListChanged != null) - { - OnListChanged(new NetworkListEvent - { - Type = eventType, - Index = index, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkListEvent() - { - Type = eventType, - Index = index, - Value = value - }); - } - } - break; - case NetworkListEvent.EventType.Clear: - { - //Read nothing - m_List.Clear(); - - if (OnListChanged != null) - { - OnListChanged(new NetworkListEvent - { - Type = eventType, - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkListEvent() - { - Type = eventType - }); - } - } - break; - } - } - } - - /// public override void ReadField(ref FastBufferReader reader) { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs index 8768ab1aea..9f8bc01667 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs @@ -68,36 +68,6 @@ public override bool IsDirty() return base.IsDirty() || m_DirtyEvents.Count > 0; } - /// - public override void WriteDelta(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteUInt16Packed((ushort)m_DirtyEvents.Count); - for (int i = 0; i < m_DirtyEvents.Count; i++) - { - writer.WriteBits((byte)m_DirtyEvents[i].Type, 2); - - switch (m_DirtyEvents[i].Type) - { - case NetworkSetEvent.EventType.Add: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Value); //BOX - } - break; - case NetworkSetEvent.EventType.Remove: - { - writer.WriteObjectPacked(m_DirtyEvents[i].Value); //BOX - } - break; - case NetworkSetEvent.EventType.Clear: - { - //Nothing has to be written - } - break; - } - } - } - /// public override void WriteField(ref FastBufferWriter writer) { @@ -138,115 +108,6 @@ public override void WriteDelta(ref FastBufferWriter writer) } } - /// - public override void WriteField(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteUInt16Packed((ushort)m_Set.Count); - - foreach (T value in m_Set) - { - writer.WriteObjectPacked(value); //BOX - } - } - - /// - public override void ReadField(Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - m_Set.Clear(); - ushort count = reader.ReadUInt16Packed(); - - for (int i = 0; i < count; i++) - { - m_Set.Add((T)reader.ReadObjectPacked(typeof(T))); //BOX - } - } - - /// - public override void ReadDelta(Stream stream, bool keepDirtyDelta) - { - using var reader = PooledNetworkReader.Get(stream); - ushort deltaCount = reader.ReadUInt16Packed(); - for (int i = 0; i < deltaCount; i++) - { - var eventType = (NetworkSetEvent.EventType)reader.ReadBits(2); - switch (eventType) - { - case NetworkSetEvent.EventType.Add: - { - var value = (T)reader.ReadObjectPacked(typeof(T)); //BOX - m_Set.Add(value); - - if (OnSetChanged != null) - { - OnSetChanged(new NetworkSetEvent - { - Type = eventType, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkSetEvent() - { - Type = eventType, - Value = value - }); - } - } - break; - case NetworkSetEvent.EventType.Remove: - { - var value = (T)reader.ReadObjectPacked(typeof(T)); //BOX - m_Set.Remove(value); - - if (OnSetChanged != null) - { - OnSetChanged(new NetworkSetEvent - { - Type = eventType, - Value = value - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkSetEvent() - { - Type = eventType, - Value = value - }); - } - } - break; - case NetworkSetEvent.EventType.Clear: - { - //Read nothing - m_Set.Clear(); - - if (OnSetChanged != null) - { - OnSetChanged(new NetworkSetEvent - { - Type = eventType, - }); - } - - if (keepDirtyDelta) - { - m_DirtyEvents.Add(new NetworkSetEvent() - { - Type = eventType - }); - } - } - break; - } - } - } - /// public override void ReadField(ref FastBufferReader reader) { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs index d8c43e7727..3721af4dc9 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs @@ -95,15 +95,6 @@ private protected void Set(T value) OnValueChanged?.Invoke(previousValue, m_InternalValue); } - /// - /// Writes the variable to the writer - /// - /// The stream to write the value to - public override void WriteDelta(Stream stream) - { - WriteField(stream); - } - /// /// Writes the variable to the writer /// @@ -113,24 +104,6 @@ public override void WriteDelta(ref FastBufferWriter writer) WriteField(ref writer); } - /// - /// Reads value from the reader and applies it - /// - /// The stream to read the value from - /// Whether or not the container should keep the dirty delta, or mark the delta as consumed - public override void ReadDelta(Stream stream, bool keepDirtyDelta) - { - using var reader = PooledNetworkReader.Get(stream); - T previousValue = m_InternalValue; - m_InternalValue = (T)reader.ReadObjectPacked(typeof(T)); - - if (keepDirtyDelta) - { - m_IsDirty = true; - } - - OnValueChanged?.Invoke(previousValue, m_InternalValue); - } /// /// Reads value from the reader and applies it @@ -150,25 +123,12 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) OnValueChanged?.Invoke(previousValue, m_InternalValue); } - /// - public override void ReadField(Stream stream) - { - ReadDelta(stream, false); - } - /// public override void ReadField(ref FastBufferReader reader) { ReadDelta(ref reader, false); } - /// - public override void WriteField(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteObjectPacked(m_InternalValue); //BOX - } - /// public override void WriteField(ref FastBufferWriter writer) { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index db8f263771..055d67e2d1 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -94,18 +94,6 @@ public virtual bool CanClientWrite(ulong clientId) return false; } - /// - /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer - /// - /// The stream to write the dirty changes to - public abstract void WriteDelta(Stream stream); - - /// - /// Writes the complete state of the variable to the writer - /// - /// The stream to write the state to - public abstract void WriteField(Stream stream); - /// /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer /// @@ -118,19 +106,6 @@ public virtual bool CanClientWrite(ulong clientId) /// The stream to write the state to public abstract void WriteField(ref FastBufferWriter writer); - /// - /// Reads the complete state from the reader and applies it - /// - /// The stream to read the state from - public abstract void ReadField(Stream stream); - - /// - /// Reads delta from the reader and applies them to the internal value - /// - /// The stream to read the delta from - /// Whether or not the delta should be kept as dirty or consumed - public abstract void ReadDelta(Stream stream, bool keepDirtyDelta); - /// /// Reads the complete state from the reader and applies it /// diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs b/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs deleted file mode 100644 index bf3bab1500..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.IO; -using Unity.Profiling; - -namespace Unity.Netcode -{ - internal class InternalMessageHandlerProfilingDecorator : IInternalMessageHandler - { - private readonly ProfilerMarker m_HandleSceneEvent = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleSceneEvent)}"); - private readonly ProfilerMarker m_HandleUnnamedMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleUnnamedMessage)}"); - private readonly ProfilerMarker m_HandleNamedMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNamedMessage)}"); - private readonly ProfilerMarker m_MessageReceiveQueueItemServerRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.{nameof(MessageQueueContainer.MessageType.ServerRpc)}"); - private readonly ProfilerMarker m_MessageReceiveQueueItemClientRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.{nameof(MessageQueueContainer.MessageType.ClientRpc)}"); - private readonly ProfilerMarker m_MessageReceiveQueueItemInternalMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(MessageReceiveQueueItem)}.InternalMessage"); - - private readonly IInternalMessageHandler m_MessageHandler; - - internal InternalMessageHandlerProfilingDecorator(IInternalMessageHandler messageHandler) - { - m_MessageHandler = messageHandler; - } - - public NetworkManager NetworkManager => m_MessageHandler.NetworkManager; - - public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType) - { - switch (messageType) - { - case MessageQueueContainer.MessageType.ServerRpc: - m_MessageReceiveQueueItemServerRpc.Begin(); - break; - case MessageQueueContainer.MessageType.ClientRpc: - m_MessageReceiveQueueItemClientRpc.Begin(); - break; - default: - m_MessageReceiveQueueItemInternalMessage.Begin(); - break; - } - - m_MessageHandler.MessageReceiveQueueItem(clientId, stream, receiveTime, messageType); - - switch (messageType) - { - case MessageQueueContainer.MessageType.ServerRpc: - m_MessageReceiveQueueItemServerRpc.End(); - break; - case MessageQueueContainer.MessageType.ClientRpc: - m_MessageReceiveQueueItemClientRpc.End(); - break; - default: - m_MessageReceiveQueueItemInternalMessage.End(); - break; - } - } - - public void HandleUnnamedMessage(ulong clientId, Stream stream) - { - m_HandleUnnamedMessage.Begin(); - - m_MessageHandler.HandleUnnamedMessage(clientId, stream); - - m_HandleUnnamedMessage.End(); - } - - public void HandleNamedMessage(ulong clientId, Stream stream) - { - m_HandleNamedMessage.Begin(); - - m_MessageHandler.HandleNamedMessage(clientId, stream); - - m_HandleNamedMessage.End(); - } - - public void HandleSceneEvent(ulong clientId, Stream stream) - { - m_HandleSceneEvent.Begin(); - - m_MessageHandler.HandleSceneEvent(clientId, stream); - - m_HandleSceneEvent.End(); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs.meta b/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs.meta deleted file mode 100644 index 3eff6cb487..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c8f43d517df0d304cbdba69447759009 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 00bed26a3c..39c1779816 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Linq; +using Unity.Netcode.Messages; using UnityEngine; using UnityEngine.SceneManagement; @@ -87,7 +88,6 @@ public class SceneEvent /// public class NetworkSceneManager : IDisposable { - private const MessageQueueContainer.MessageType k_MessageType = MessageQueueContainer.MessageType.SceneEvent; private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableSequenced; private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; @@ -423,26 +423,14 @@ private void SendSceneEventData(ulong[] targetClientIds) // Silently return as there is nothing to be done return; } - - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - k_MessageType, k_DeliveryType, targetClientIds, k_NetworkUpdateStage); - - if (context != null) + var message = new SceneEventMessage { - using var nonNullContext = (InternalCommandContext)context; - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - SceneEventData.OnWrite(nonNullContext.NetworkWriter); - - var size = bufferSizeCapture.StopMeasureSegment(); - m_NetworkManager.NetworkMetrics.TrackSceneEventSent( - targetClientIds, (uint)SceneEventData.SceneEventType, ScenesInBuild[(int)SceneEventData.SceneIndex], size); - return; - } + EventData = SceneEventData + }; + var size = m_NetworkManager.SendMessage(message, k_DeliveryType, targetClientIds); - // This should never happen, but if it does something very bad has happened and we should throw an exception - throw new Exception($"{nameof(InternalCommandContext)} is null! {nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientIds {targetClientIds}!"); + m_NetworkManager.NetworkMetrics.TrackSceneEventSent( + targetClientIds, (uint)SceneEventData.SceneEventType, ScenesInBuild[(int)SceneEventData.SceneIndex], size); } /// @@ -584,30 +572,24 @@ private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading /// private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress) { - // Send a message to all clients that all clients are done loading or unloading - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - k_MessageType, k_DeliveryType, m_NetworkManager.ConnectedClientsIds, k_NetworkUpdateStage); - if (context != null) - { - using var nonNullContext = (InternalCommandContext)context; - ClientSynchEventData.SceneEventGuid = sceneEventProgress.Guid; - ClientSynchEventData.SceneIndex = sceneEventProgress.SceneBuildIndex; - ClientSynchEventData.SceneEventType = sceneEventProgress.SceneEventType; - ClientSynchEventData.ClientsCompleted = sceneEventProgress.DoneClients; - ClientSynchEventData.ClientsTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(); - - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); - - var size = bufferSizeCapture.StopMeasureSegment(); - m_NetworkManager.NetworkMetrics.TrackSceneEventSent( - m_NetworkManager.ConnectedClientsIds, - (uint)sceneEventProgress.SceneEventType, - ScenesInBuild[(int)sceneEventProgress.SceneBuildIndex], - size); - } + + ClientSynchEventData.SceneEventGuid = sceneEventProgress.Guid; + ClientSynchEventData.SceneIndex = sceneEventProgress.SceneBuildIndex; + ClientSynchEventData.SceneEventType = sceneEventProgress.SceneEventType; + ClientSynchEventData.ClientsCompleted = sceneEventProgress.DoneClients; + ClientSynchEventData.ClientsTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(); + + var message = new SceneEventMessage + { + EventData = ClientSynchEventData + }; + var size = m_NetworkManager.SendMessage(message, k_DeliveryType, m_NetworkManager.ConnectedClientsIds); + + m_NetworkManager.NetworkMetrics.TrackSceneEventSent( + m_NetworkManager.ConnectedClientsIds, + (uint)sceneEventProgress.SceneEventType, + ScenesInBuild[(int)sceneEventProgress.SceneBuildIndex], + size); // Send a local notification to the server that all clients are done loading or unloading OnSceneEvent?.Invoke(new SceneEvent() @@ -892,7 +874,7 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc /// Handles both forms of scene loading /// /// Stream data associated with the event - private void OnClientSceneLoadingEvent(Stream objectStream) + private void OnClientSceneLoadingEvent() { if (!IsSceneIndexValid(SceneEventData.SceneIndex)) { @@ -1038,7 +1020,7 @@ private void OnServerLoadedScene(Scene scene) { if (!keyValuePairBySceneHandle.Value.IsPlayerObject) { - m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, null, false, true); + m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, true); } } } @@ -1052,26 +1034,14 @@ private void OnServerLoadedScene(Scene scene) var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId; if (clientId != m_NetworkManager.ServerClientId) { - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - k_MessageType, k_DeliveryType, new ulong[] { clientId }, k_NetworkUpdateStage); - if (context != null) + SceneEventData.TargetClientId = clientId; + var message = new SceneEventMessage { - // Set the target client id that will be used during in scene NetworkObject serialization - SceneEventData.TargetClientId = clientId; - using var nonNullContext = (InternalCommandContext)context; + EventData = SceneEventData + }; + var size = m_NetworkManager.SendMessage(message, k_DeliveryType, clientId); - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - SceneEventData.OnWrite(nonNullContext.NetworkWriter); - - var size = bufferSizeCapture.StopMeasureSegment(); - m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)SceneEventData.SceneEventType, scene.name, size); - } - else - { - throw new Exception($"{nameof(NetworkSceneManager)} failed to send event notification {SceneEventData.SceneEventType} to target clientId {clientId}!"); - } + m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)SceneEventData.SceneEventType, scene.name, size); } } @@ -1167,26 +1137,18 @@ internal void SynchronizeNetworkObjects(ulong clientId) ClientSynchEventData.AddSpawnedNetworkObjects(); - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - k_MessageType, k_DeliveryType, new ulong[] { clientId }, k_NetworkUpdateStage); - if (context != null) + var message = new SceneEventMessage { + EventData = ClientSynchEventData + }; + var size = m_NetworkManager.SendMessage(message, k_DeliveryType, clientId); - using var nonNullContext = (InternalCommandContext)context; - - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); - - var size = bufferSizeCapture.StopMeasureSegment(); - foreach (var sceneIndex in ClientSynchEventData.ScenesToSynchronize) - { - m_NetworkManager.NetworkMetrics.TrackSceneEventSent( - clientId, (uint)ClientSynchEventData.SceneEventType, ScenesInBuild[(int)sceneIndex], size); - } + foreach (var sceneIndex in ClientSynchEventData.ScenesToSynchronize) + { + m_NetworkManager.NetworkMetrics.TrackSceneEventSent( + clientId, (uint) ClientSynchEventData.SceneEventType, ScenesInBuild[(int) sceneIndex], size); } - + // Notify the local server that the client has been sent the SceneEventData.SceneEventTypes.S2C_Event_Sync event OnSceneEvent?.Invoke(new SceneEvent() { @@ -1319,21 +1281,15 @@ private void ClientLoadedSynchronization(uint sceneIndex, int sceneHandle) ClientSynchEventData.LoadSceneMode = loadSceneMode; ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete; ClientSynchEventData.SceneIndex = sceneIndex; - - var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext( - k_MessageType, k_DeliveryType, new ulong[] { m_NetworkManager.ServerClientId }, k_NetworkUpdateStage); - if (context != null) + + + var message = new SceneEventMessage { - using var nonNullContext = (InternalCommandContext)context; + EventData = ClientSynchEventData + }; + var size = m_NetworkManager.SendMessage(message, k_DeliveryType, m_NetworkManager.ServerClientId); - var bufferSizeCapture = new CommandContextSizeCapture(nonNullContext); - bufferSizeCapture.StartMeasureSegment(); - - ClientSynchEventData.OnWrite(nonNullContext.NetworkWriter); - - var size = bufferSizeCapture.StopMeasureSegment(); - m_NetworkManager.NetworkMetrics.TrackSceneEventSent(m_NetworkManager.ServerClientId, (uint)ClientSynchEventData.SceneEventType, sceneName, size); - } + m_NetworkManager.NetworkMetrics.TrackSceneEventSent(m_NetworkManager.ServerClientId, (uint)ClientSynchEventData.SceneEventType, sceneName, size); // Send notification to local client that the scene has finished loading OnSceneEvent?.Invoke(new SceneEvent() @@ -1346,21 +1302,20 @@ private void ClientLoadedSynchronization(uint sceneIndex, int sceneHandle) }); // Check to see if we still have scenes to load and synchronize with - HandleClientSceneEvent(null); + HandleClientSceneEvent(); } /// /// Client Side: /// Handles incoming Scene_Event messages for clients /// - /// data associated with the event - private void HandleClientSceneEvent(Stream stream) + private void HandleClientSceneEvent() { switch (SceneEventData.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Load: { - OnClientSceneLoadingEvent(stream); + OnClientSceneLoadingEvent(); break; } case SceneEventData.SceneEventTypes.S2C_Unload: @@ -1438,8 +1393,7 @@ private void HandleClientSceneEvent(Stream stream) /// Handles incoming Scene_Event messages for host or server /// /// client who sent the event - /// data associated with the event - private void HandleServerSceneEvent(ulong clientId, Stream stream) + private void HandleServerSceneEvent(ulong clientId) { switch (SceneEventData.SceneEventType) { @@ -1520,47 +1474,37 @@ private void HandleServerSceneEvent(ulong clientId, Stream stream) /// /// client who sent the scene event /// data associated with the scene event - internal void HandleSceneEvent(ulong clientId, Stream stream) + internal void HandleSceneEvent(ulong clientId, ref FastBufferReader reader) { if (m_NetworkManager != null) { - if (stream != null) - { - var reader = NetworkReaderPool.GetReader(stream); - SceneEventData.OnRead(reader); - NetworkReaderPool.PutBackInPool(reader); + SceneEventData.Deserialize(ref reader); - if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.S2C_Sync) - { - // For this event the server may be sending scene event data about multiple scenes, so we need - // to track a metric for each one. - foreach (var sceneIndex in SceneEventData.ScenesToSynchronize) - { - m_NetworkManager.NetworkMetrics.TrackSceneEventReceived( - clientId, (uint)SceneEventData.SceneEventType, ScenesInBuild[(int)sceneIndex], stream.Length); - } - } - else + if (SceneEventData.SceneEventType == SceneEventData.SceneEventTypes.S2C_Sync) + { + // For this event the server may be sending scene event data about multiple scenes, so we need + // to track a metric for each one. + foreach (var sceneIndex in SceneEventData.ScenesToSynchronize) { - // For all other scene event types, we are only dealing with one scene at a time, so we can read it - // from the SceneEventData directly. m_NetworkManager.NetworkMetrics.TrackSceneEventReceived( - clientId, (uint)SceneEventData.SceneEventType, ScenesInBuild[(int)SceneEventData.SceneIndex], stream.Length); + clientId, (uint)SceneEventData.SceneEventType, ScenesInBuild[(int)sceneIndex], reader.Length); } + } + else + { + // For all other scene event types, we are only dealing with one scene at a time, so we can read it + // from the SceneEventData directly. + m_NetworkManager.NetworkMetrics.TrackSceneEventReceived( + clientId, (uint)SceneEventData.SceneEventType, ScenesInBuild[(int)SceneEventData.SceneIndex], reader.Length); + } - if (SceneEventData.IsSceneEventClientSide()) - { - HandleClientSceneEvent(stream); - } - else - { - HandleServerSceneEvent(clientId, stream); - } + if (SceneEventData.IsSceneEventClientSide()) + { + HandleClientSceneEvent(); } else { - Debug.LogError($"Scene Event {nameof(OnClientSceneLoadingEvent)} was invoked with a null stream!"); - return; + HandleServerSceneEvent(clientId); } } else diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 4ac904dd2a..162ce92e41 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System; using System.Linq; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.SceneManagement; @@ -19,7 +21,7 @@ public class SceneEventData : IDisposable /// A Server To Client Event (S2C) /// A Client to Server Event (C2S) /// - public enum SceneEventTypes + public enum SceneEventTypes : byte { /// /// Load a scene @@ -119,7 +121,8 @@ public enum SceneEventTypes /// private List m_NetworkObjectsToBeRemoved = new List(); - internal PooledNetworkBuffer InternalBuffer; + private bool m_HasInternalBuffer; + internal FastBufferReader InternalBuffer; private NetworkManager m_NetworkManager; @@ -305,50 +308,50 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) /// Serializes data based on the SceneEvent type () /// /// to write the scene event data - internal void OnWrite(NetworkWriter writer) + internal void Serialize(ref FastBufferWriter writer) { // Write the scene event type - writer.WriteByte((byte)SceneEventType); + writer.WriteValueSafe(SceneEventType); // Write the scene loading mode - writer.WriteByte((byte)LoadSceneMode); + writer.WriteValueSafe(LoadSceneMode); // Write the scene event progress Guid if (SceneEventType != SceneEventTypes.S2C_Sync) { - writer.WriteByteArray(SceneEventGuid.ToByteArray()); + writer.WriteValueSafe(SceneEventGuid); } // Write the scene index and handle - writer.WriteUInt32Packed(SceneIndex); - writer.WriteInt32Packed(SceneHandle); + writer.WriteValueSafe(SceneIndex); + writer.WriteValueSafe(SceneHandle); switch (SceneEventType) { case SceneEventTypes.S2C_Sync: { - WriteSceneSynchronizationData(writer); + WriteSceneSynchronizationData(ref writer); break; } case SceneEventTypes.S2C_Load: { - SerializeScenePlacedObjects(writer); + SerializeScenePlacedObjects(ref writer); break; } case SceneEventTypes.C2S_SyncComplete: { - WriteClientSynchronizationResults(writer); + WriteClientSynchronizationResults(ref writer); break; } case SceneEventTypes.S2C_ReSync: { - WriteClientReSynchronizationData(writer); + WriteClientReSynchronizationData(ref writer); break; } case SceneEventTypes.S2C_LoadComplete: case SceneEventTypes.S2C_UnLoadComplete: { - WriteSceneEventProgressDone(writer); + WriteSceneEventProgressDone(ref writer); break; } } @@ -359,40 +362,41 @@ internal void OnWrite(NetworkWriter writer) /// Called at the end of an S2C_Load event once the scene is loaded and scene placed NetworkObjects /// have been locally spawned /// - internal void WriteSceneSynchronizationData(NetworkWriter writer) + internal void WriteSceneSynchronizationData(ref FastBufferWriter writer) { // Write the scenes we want to load, in the order we want to load them - writer.WriteUIntArrayPacked(ScenesToSynchronize.ToArray()); - writer.WriteUIntArrayPacked(SceneHandlesToSynchronize.ToArray()); + writer.WriteValueSafe(ScenesToSynchronize.ToArray()); + writer.WriteValueSafe(SceneHandlesToSynchronize.ToArray()); + // Store our current position in the stream to come back and say how much data we have written - var positionStart = writer.GetStream().Position; + var positionStart = writer.Position; // Size Place Holder -- Start // !!NOTE!!: Since this is a placeholder to be set after we know how much we have written, // for stream offset purposes this MUST not be a packed value! - writer.WriteUInt32(0); - var totalBytes = 0; + writer.WriteValueSafe((int)0); + int totalBytes = 0; // Write the number of NetworkObjects we are serializing - writer.WriteInt32Packed(m_NetworkObjectsSync.Count()); - - foreach (var networkObject in m_NetworkObjectsSync) - { - var noStart = writer.GetStream().Position; - writer.WriteInt32Packed(networkObject.gameObject.scene.handle); - networkObject.SerializeSceneObject(writer, TargetClientId); - var noStop = writer.GetStream().Position; + writer.WriteValueSafe(m_NetworkObjectsSync.Count()); + for(var i = 0; i < m_NetworkObjectsSync.Count(); ++i) + { + var noStart = writer.Position; + var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId); + writer.WriteValueSafe(m_NetworkObjectsSync[i].gameObject.scene.handle); + sceneObject.Serialize(ref writer); + var noStop = writer.Position; totalBytes += (int)(noStop - noStart); } // Size Place Holder -- End - var positionEnd = writer.GetStream().Position; + var positionEnd = writer.Position; var bytesWritten = (uint)(positionEnd - (positionStart + sizeof(uint))); - writer.GetStream().Position = positionStart; + writer.Seek(positionStart); // Write the total size written to the stream by NetworkObjects being serialized - writer.WriteUInt32(bytesWritten); - writer.GetStream().Position = positionEnd; + writer.WriteValueSafe(bytesWritten); + writer.Seek(positionEnd); } /// @@ -401,14 +405,13 @@ internal void WriteSceneSynchronizationData(NetworkWriter writer) /// have been locally spawned /// Maximum number of objects that could theoretically be synchronized is 65536 /// - internal void SerializeScenePlacedObjects(NetworkWriter writer) + internal void SerializeScenePlacedObjects(ref FastBufferWriter writer) { var numberOfObjects = (ushort)0; - var stream = writer.GetStream(); - var headPosition = stream.Position; + var headPosition = writer.Position; // Write our count place holder (must not be packed!) - writer.WriteUInt16(0); + writer.WriteValueSafe((ushort)0); foreach (var keyValuePairByGlobalObjectIdHash in m_NetworkManager.SceneManager.ScenePlacedObjects) { @@ -417,21 +420,22 @@ internal void SerializeScenePlacedObjects(NetworkWriter writer) if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId)) { // Write our server relative scene handle for the NetworkObject being serialized - writer.WriteInt32Packed(keyValuePairBySceneHandle.Key); + writer.WriteValueSafe(keyValuePairBySceneHandle.Key); // Serialize the NetworkObject - keyValuePairBySceneHandle.Value.SerializeSceneObject(writer, TargetClientId); + var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId); + sceneObject.Serialize(ref writer); numberOfObjects++; } } } - var tailPosition = stream.Position; + var tailPosition = writer.Position; // Reposition to our count position to the head before we wrote our object count - stream.Position = headPosition; + writer.Seek(headPosition); // Write number of NetworkObjects serialized (must not be packed!) - writer.WriteUInt16(numberOfObjects); + writer.WriteValueSafe(numberOfObjects); // Set our position back to the tail - stream.Position = tailPosition; + writer.Seek(tailPosition); } /// @@ -439,69 +443,51 @@ internal void SerializeScenePlacedObjects(NetworkWriter writer) /// Deserialize data based on the SceneEvent type. /// /// - internal void OnRead(NetworkReader reader) + internal void Deserialize(ref FastBufferReader reader) { - var sceneEventTypeValue = reader.ReadByte(); - - if (Enum.IsDefined(typeof(SceneEventTypes), sceneEventTypeValue)) - { - SceneEventType = (SceneEventTypes)sceneEventTypeValue; - } - else - { - Debug.LogError($"Serialization Read Error: {nameof(SceneEventType)} vale {sceneEventTypeValue} is not within the range of the defined {nameof(SceneEventTypes)} enumerator!"); - } - - var loadSceneModeValue = reader.ReadByte(); - - if (Enum.IsDefined(typeof(LoadSceneMode), loadSceneModeValue)) - { - LoadSceneMode = (LoadSceneMode)loadSceneModeValue; - } - else - { - Debug.LogError($"Serialization Read Error: {nameof(LoadSceneMode)} vale {loadSceneModeValue} is not within the range of the defined {nameof(LoadSceneMode)} enumerator!"); - } + reader.ReadValueSafe(out SceneEventType); + reader.ReadValueSafe(out LoadSceneMode); if (SceneEventType != SceneEventTypes.S2C_Sync) { - SceneEventGuid = new Guid(reader.ReadByteArray()); + reader.ReadValueSafe(out SceneEventGuid); } - SceneIndex = reader.ReadUInt32Packed(); - SceneHandle = reader.ReadInt32Packed(); + reader.ReadValueSafe(out SceneIndex); + reader.ReadValueSafe(out SceneHandle); switch (SceneEventType) { case SceneEventTypes.S2C_Sync: { - CopySceneSyncrhonizationData(reader); + CopySceneSyncrhonizationData(ref reader); break; } case SceneEventTypes.C2S_SyncComplete: { - CheckClientSynchronizationResults(reader); + CheckClientSynchronizationResults(ref reader); break; } case SceneEventTypes.S2C_Load: { - SetInternalBuffer(); - // We store off the trailing in-scene placed serialized NetworkObject data to - // be processed once we are done loading. - InternalBuffer.Position = 0; - InternalBuffer.CopyUnreadFrom(reader.GetStream()); - InternalBuffer.Position = 0; + unsafe + { + // We store off the trailing in-scene placed serialized NetworkObject data to + // be processed once we are done loading. + m_HasInternalBuffer = true; + InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, reader.Length + reader.Position); + } break; } case SceneEventTypes.S2C_ReSync: { - ReadClientReSynchronizationData(reader); + ReadClientReSynchronizationData(ref reader); break; } case SceneEventTypes.S2C_LoadComplete: case SceneEventTypes.S2C_UnLoadComplete: { - ReadSceneEventProgressDone(reader); + ReadSceneEventProgressDone(ref reader); break; } } @@ -513,21 +499,26 @@ internal void OnRead(NetworkReader reader) /// into the internal buffer to be used throughout the synchronization process. /// /// - internal void CopySceneSyncrhonizationData(NetworkReader reader) + internal void CopySceneSyncrhonizationData(ref FastBufferReader reader) { - SetInternalBuffer(); m_NetworkObjectsSync.Clear(); - ScenesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); - SceneHandlesToSynchronize = new Queue(reader.ReadUIntArrayPacked()); - InternalBuffer.Position = 0; + reader.ReadValueSafe(out uint[] scenesToSynchronize); + reader.ReadValueSafe(out uint[] sceneHandlesToSynchronize); + ScenesToSynchronize = new Queue(scenesToSynchronize); + SceneHandlesToSynchronize = new Queue(sceneHandlesToSynchronize); // is not packed! - var sizeToCopy = reader.ReadUInt32(); - - using var writer = PooledNetworkWriter.Get(InternalBuffer); - writer.ReadAndWrite(reader, (long)sizeToCopy); - - InternalBuffer.Position = 0; + reader.ReadValueSafe(out int sizeToCopy); + unsafe + { + if (!reader.TryBeginRead(sizeToCopy)) + { + throw new OverflowException("Not enough space in the buffer to read recorded synchronization data size."); + } + + m_HasInternalBuffer = true; + InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, sizeToCopy); + } } /// @@ -537,19 +528,28 @@ internal void CopySceneSyncrhonizationData(NetworkReader reader) /// internal void DeserializeScenePlacedObjects() { - using var reader = PooledNetworkReader.Get(InternalBuffer); - // is not packed! - var newObjectsCount = reader.ReadUInt16(); - - for (ushort i = 0; i < newObjectsCount; i++) + try { - // Set our relative scene to the NetworkObject - m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); - - // Deserialize the NetworkObject - NetworkObject.DeserializeSceneObject(InternalBuffer as NetworkBuffer, reader, m_NetworkManager); + // is not packed! + InternalBuffer.ReadValueSafe(out uint newObjectsCount); + + for (ushort i = 0; i < newObjectsCount; i++) + { + InternalBuffer.ReadValueSafe(out int sceneHandle); + // Set our relative scene to the NetworkObject + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneHandle); + + // Deserialize the NetworkObject + NetworkObject.SceneObject sceneObject = new NetworkObject.SceneObject(); + sceneObject.Deserialize(ref InternalBuffer); + NetworkObject.AddSceneObject(sceneObject, ref InternalBuffer, m_NetworkManager); + } + } + finally + { + InternalBuffer.Dispose(); + m_HasInternalBuffer = false; } - ReleaseInternalBuffer(); } /// @@ -559,9 +559,9 @@ internal void DeserializeScenePlacedObjects() /// client handles any returned values by the server. /// /// - internal void ReadClientReSynchronizationData(NetworkReader reader) + internal void ReadClientReSynchronizationData(ref FastBufferReader reader) { - var networkObjectsToRemove = reader.ReadULongArrayPacked(); + reader.ReadValueSafe(out uint[] networkObjectsToRemove); if (networkObjectsToRemove.Length > 0) { @@ -613,10 +613,10 @@ internal void ReadClientReSynchronizationData(NetworkReader reader) /// the server will compile a list and send back an Event_ReSync message to the client. /// /// - internal void WriteClientReSynchronizationData(NetworkWriter writer) + internal void WriteClientReSynchronizationData(ref FastBufferWriter writer) { //Write how many objects need to be removed - writer.WriteULongArrayPacked(m_NetworkObjectsToBeRemoved.ToArray()); + writer.WriteValueSafe(m_NetworkObjectsToBeRemoved.ToArray()); } /// @@ -637,13 +637,13 @@ internal bool ClientNeedsReSynchronization() /// have since been despawned. /// /// - internal void CheckClientSynchronizationResults(NetworkReader reader) + internal void CheckClientSynchronizationResults(ref FastBufferReader reader) { m_NetworkObjectsToBeRemoved.Clear(); - var networkObjectIdCount = reader.ReadUInt32Packed(); + reader.ReadValueSafe(out uint networkObjectIdCount); for (int i = 0; i < networkObjectIdCount; i++) { - var networkObjectId = (ulong)reader.ReadUInt32Packed(); + reader.ReadValueSafe(out uint networkObjectId); if (!m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) { m_NetworkObjectsToBeRemoved.Add(networkObjectId); @@ -659,13 +659,13 @@ internal void CheckClientSynchronizationResults(NetworkReader reader) /// of NetworkObjects that might have been despawned while the client was processing the Event_Sync. /// /// - internal void WriteClientSynchronizationResults(NetworkWriter writer) + internal void WriteClientSynchronizationResults(ref FastBufferWriter writer) { //Write how many objects were spawned - writer.WriteUInt32Packed((uint)m_NetworkObjectsSync.Count); + writer.WriteValueSafe((uint)m_NetworkObjectsSync.Count); foreach (var networkObject in m_NetworkObjectsSync) { - writer.WriteUInt32Packed((uint)networkObject.NetworkObjectId); + writer.WriteValueSafe((uint)networkObject.NetworkObjectId); } } @@ -679,42 +679,52 @@ internal void WriteClientSynchronizationResults(NetworkWriter writer) /// internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { - using var reader = PooledNetworkReader.Get(InternalBuffer); - // Process all NetworkObjects for this scene - var newObjectsCount = reader.ReadInt32Packed(); - - for (int i = 0; i < newObjectsCount; i++) + try { - /// We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is - /// currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject - /// from the list of populated - m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); + // Process all NetworkObjects for this scene + InternalBuffer.ReadValueSafe(out int newObjectsCount); - var spawnedNetworkObject = NetworkObject.DeserializeSceneObject(InternalBuffer, reader, networkManager); - if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) + for (int i = 0; i < newObjectsCount; i++) { - m_NetworkObjectsSync.Add(spawnedNetworkObject); + /// We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is + /// currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject + /// from the list of populated + InternalBuffer.ReadValueSafe(out int handle); + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(handle); + + NetworkObject.SceneObject sceneObject = new NetworkObject.SceneObject(); + sceneObject.Deserialize(ref InternalBuffer); + + var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, ref InternalBuffer, networkManager); + if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) + { + m_NetworkObjectsSync.Add(spawnedNetworkObject); + } } } - ReleaseInternalBuffer(); + finally + { + InternalBuffer.Dispose(); + m_HasInternalBuffer = false; + } } /// /// Writes the all clients loaded or unloaded completed and timed out lists /// /// - internal void WriteSceneEventProgressDone(NetworkWriter writer) + internal void WriteSceneEventProgressDone(ref FastBufferWriter writer) { - writer.WriteUInt16Packed((ushort)ClientsCompleted.Count); + writer.WriteValueSafe((ushort)ClientsCompleted.Count); foreach (var clientId in ClientsCompleted) { - writer.WriteUInt64Packed(clientId); + writer.WriteValueSafe(clientId); } - writer.WriteUInt16Packed((ushort)ClientsTimedOut.Count); + writer.WriteValueSafe((ushort)ClientsTimedOut.Count); foreach (var clientId in ClientsTimedOut) { - writer.WriteUInt64Packed(clientId); + writer.WriteValueSafe(clientId); } } @@ -722,43 +732,22 @@ internal void WriteSceneEventProgressDone(NetworkWriter writer) /// Reads the all clients loaded or unloaded completed and timed out lists /// /// - internal void ReadSceneEventProgressDone(NetworkReader reader) + internal void ReadSceneEventProgressDone(ref FastBufferReader reader) { - var completedCount = reader.ReadUInt16Packed(); + reader.ReadValueSafe(out ushort completedCount); ClientsCompleted = new List(); for (int i = 0; i < completedCount; i++) { - ClientsCompleted.Add(reader.ReadUInt64Packed()); + reader.ReadValue(out ulong clientId); + ClientsCompleted.Add(clientId); } - var timedOutCount = reader.ReadUInt16Packed(); + reader.ReadValueSafe(out ushort timedOutCount); ClientsTimedOut = new List(); for (int i = 0; i < timedOutCount; i++) { - ClientsTimedOut.Add(reader.ReadUInt64Packed()); - } - } - - /// - /// Gets a PooledNetworkBuffer if needed - /// - private void SetInternalBuffer() - { - if (InternalBuffer == null) - { - InternalBuffer = NetworkBufferPool.GetBuffer(); - } - } - - /// - /// Releases the PooledNetworkBuffer when no longer needed - /// - private void ReleaseInternalBuffer() - { - if (InternalBuffer != null) - { - NetworkBufferPool.PutBackInPool(InternalBuffer); - InternalBuffer = null; + reader.ReadValue(out ulong clientId); + ClientsTimedOut.Add(clientId); } } @@ -767,7 +756,11 @@ private void ReleaseInternalBuffer() /// public void Dispose() { - ReleaseInternalBuffer(); + if (m_HasInternalBuffer) + { + InternalBuffer.Dispose(); + m_HasInternalBuffer = false; + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs deleted file mode 100644 index 0e496ba57a..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Unity.Netcode -{ - /// - /// AutoBitWritable implements INetworkSerializable and automatically serializes fields using reflection - /// - public abstract class AutoNetworkSerializable : INetworkSerializable - { - private void Write(NetworkWriter writer) - { - var fields = SerializationManager.GetFieldsForType(GetType()); - for (int i = 0; i < fields.Length; i++) - { - writer.WriteObjectPacked(fields[i].GetValue(this)); - } - } - - private void Read(NetworkReader reader) - { - var fields = SerializationManager.GetFieldsForType(GetType()); - for (int i = 0; i < fields.Length; i++) - { - fields[i].SetValue(this, reader.ReadObjectPacked(fields[i].FieldType)); - } - } - - public void NetworkSerialize(NetworkSerializer serializer) - { - if (serializer.IsReading) - { - Read(serializer.Reader); - } - else - { - Write(serializer.Writer); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs.meta deleted file mode 100644 index 9680ca664a..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/AutoNetworkSerializable.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ffcc1ff68a0732d41b80725a9d665e1c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index a9132cb5bd..a9c112ed8f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -84,35 +84,16 @@ public void SerializeValue(ref object value, Type type, bool isNullable = false) /// /// Serialize an INetworkSerializable - /// If your INetworkSerializable is implemented by a struct, as opposed to a class, use this - /// function instead of SerializeValue. SerializeValue will incur a boxing allocation, - /// SerializeNetworkSerializable will not. - /// - /// A definition of SerializeValue that doesn't allocate can't be created because C# - /// doesn't allow overriding generics based solely on the constraint, so this would conflict - /// with WriteValue<T>(ref T value) where T: unmanaged /// /// Throws OverflowException if the end of the buffer has been reached. /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. /// /// Value to serialize - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new() { m_Implementation.SerializeNetworkSerializable(ref value); } - /// - /// Serialize an INetworkSerializable - /// - /// Throws OverflowException if the end of the buffer has been reached. - /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. - /// - /// Value to serialize - public void SerializeValue(ref INetworkSerializable value) - { - m_Implementation.SerializeValue(ref value); - } - /// /// Serialize a GameObject /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index ae777d3735..ec64ed89ac 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -30,11 +30,6 @@ public void SerializeValue(ref object value, Type type, bool isNullable = false) m_Reader.Value.ReadObject(out value, type, isNullable); } - public void SerializeValue(ref INetworkSerializable value) - { - m_Reader.Value.ReadNetworkSerializable(out value); - } - public void SerializeValue(ref GameObject value) { m_Reader.Value.ReadValueSafe(out value); @@ -70,7 +65,7 @@ public void SerializeValue(ref T value) where T : unmanaged m_Reader.Value.ReadValueSafe(out value); } - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new() { m_Reader.Value.ReadNetworkSerializable(out value); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index f3a345ea99..e9ddd01dc0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -30,11 +30,6 @@ public void SerializeValue(ref object value, Type type, bool isNullable = false) m_Writer.Value.WriteObject(value, isNullable); } - public void SerializeValue(ref INetworkSerializable value) - { - m_Writer.Value.WriteNetworkSerializable(value); - } - public void SerializeValue(ref GameObject value) { m_Writer.Value.WriteValueSafe(value); @@ -70,7 +65,7 @@ public void SerializeValue(ref T value) where T : unmanaged m_Writer.Value.WriteValueSafe(value); } - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new() { m_Writer.Value.WriteNetworkSerializable(value); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 9eb5854e67..494b9a80da 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -100,12 +100,12 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, ((GameObject)value).TryGetComponent(out var networkObject); if (networkObject == null) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteValuePacked(ref writer, networkObject.NetworkObjectId); @@ -115,7 +115,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, { if (!((NetworkObject)value).IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); + throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); } WriteValuePacked(ref writer, ((NetworkObject)value).NetworkObjectId); @@ -125,7 +125,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, { if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkObjectId); @@ -138,7 +138,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, return; } - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + throw new ArgumentException($"{nameof(BytePacker)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } #endregion diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 85263010dc..e08df4c8d3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -378,6 +378,35 @@ public unsafe byte[] ToArray() return BufferPointer + PositionInternal; } + /// + /// Read an INetworkSerializable + /// + /// INetworkSerializable instance + /// + /// + public void ReadNetworkSerializable(out T value) where T : INetworkSerializable, new() + { + value = new T(); + var bufferSerializer = new BufferSerializer(new BufferSerializerReader(ref this)); + value.NetworkSerialize(bufferSerializer); + } + + /// + /// Read an array of INetworkSerializables + /// + /// INetworkSerializable instance + /// + /// + public void ReadNetworkSerializable(out T[] value) where T : INetworkSerializable, new() + { + ReadValueSafe(out int size); + value = new T[size]; + for(var i = 0; i < size; ++i) + { + ReadNetworkSerializable(out value[i]); + } + } + /// /// Reads a string /// NOTE: ALLOCATES diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 02f567a9df..8ba50099a9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -129,17 +129,6 @@ public static void ReadObject(this ref FastBufferReader reader, out object value throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } - /// - /// Read an INetworkSerializable - /// - /// INetworkSerializable instance - /// - /// - public static void ReadNetworkSerializable(this ref FastBufferReader reader, out T value) where T : INetworkSerializable - { - throw new NotImplementedException(); - } - /// /// Read a GameObject /// @@ -186,6 +175,20 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out GameObjec value = null; } + + /// + /// Read an array of GameObjects + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject[] value) + { + reader.ReadValueSafe(out int size); + value = new GameObject[size]; + for (var i = 0; i < size; ++i) + { + reader.ReadValueSafe(out value[i]); + } + } /// /// Read a NetworkObject @@ -233,6 +236,20 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkOb value = null; } + + /// + /// Read an array of NetworkObjects + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject[] value) + { + reader.ReadValueSafe(out int size); + value = new NetworkObject[size]; + for (var i = 0; i < size; ++i) + { + reader.ReadValueSafe(out value[i]); + } + } /// /// Read a NetworkBehaviour @@ -282,5 +299,19 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBe value = null; } + + /// + /// Read an array of NetworkBehaviours + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour[] value) + { + reader.ReadValueSafe(out int size); + value = new NetworkBehaviour[size]; + for (var i = 0; i < size; ++i) + { + reader.ReadValueSafe(out value[i]); + } + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 3137faef80..5f21ca6cda 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -365,8 +365,30 @@ public static int GetWriteSize(string s, bool oneByteChars = false) return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); } - public void WriteUnknownSafe(in T value) + /// + /// Write an INetworkSerializable + /// + /// The value to write + /// + public void WriteNetworkSerializable(in T value) where T: INetworkSerializable + { + var bufferSerializer = new BufferSerializer(new BufferSerializerWriter(ref this)); + value.NetworkSerialize(bufferSerializer); + } + + /// + /// Write an array of INetworkSerializables + /// + /// The value to write + /// + public void WriteNetworkSerializable(INetworkSerializable[] array, int count = -1, int offset = 0) where T: INetworkSerializable { + int sizeInTs = count != -1 ? count : array.Length - offset; + WriteValueSafe(sizeInTs); + foreach (var item in array) + { + WriteNetworkSerializable(item); + } } /// @@ -643,11 +665,11 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) /// Number of bytes to write /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteBytes(byte[] value, int size, int offset = 0) + public unsafe void WriteBytes(byte[] value, int size = -1, int offset = 0) { fixed (byte* ptr = value) { - WriteBytes(ptr, size, offset); + WriteBytes(ptr, size == -1 ? value.Length : size, offset); } } @@ -661,11 +683,11 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) /// Number of bytes to write /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) + public unsafe void WriteBytesSafe(byte[] value, int size = -1, int offset = 0) { fixed (byte* ptr = value) { - WriteBytesSafe(ptr, size, offset); + WriteBytesSafe(ptr, size == -1 ? value.Length : size, offset); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs index af269f811e..94240f61ac 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -111,16 +111,6 @@ public static void WriteObject(this ref FastBufferWriter writer, object value, b throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } - /// - /// Write an INetworkSerializable - /// - /// The value to write - /// - public static void WriteNetworkSerializable(this ref FastBufferWriter writer, in T value) where T : INetworkSerializable - { - // TODO - } - /// /// Get the required amount of space to write a GameObject /// @@ -144,7 +134,7 @@ public static int GetGameObjectWriteSize() /// Write a GameObject /// /// The value to write - public static void WriteValue(this ref FastBufferWriter writer, GameObject value) + public static void WriteValue(this ref FastBufferWriter writer, in GameObject value) { value.TryGetComponent(out var networkObject); if (networkObject == null) @@ -160,6 +150,21 @@ public static void WriteValue(this ref FastBufferWriter writer, GameObject value writer.WriteValue(networkObject.NetworkObjectId); } + /// + /// Write an array of GameObjects + /// + /// The value to write + /// + /// + public static void WriteValue(this ref FastBufferWriter writer, GameObject[] value) + { + writer.WriteValue((int)value.Length); + foreach (var item in value) + { + writer.WriteValue(value); + } + } + /// /// Write a GameObject /// @@ -167,7 +172,7 @@ public static void WriteValue(this ref FastBufferWriter writer, GameObject value /// for multiple writes at once by calling TryBeginWrite. /// /// The value to write - public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject value) + public static void WriteValueSafe(this ref FastBufferWriter writer, in GameObject value) { value.TryGetComponent(out var networkObject); if (networkObject == null) @@ -183,6 +188,24 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject v writer.WriteValueSafe(networkObject.NetworkObjectId); } + /// + /// Write an array of GameObjects + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// + /// + public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject[] value) + { + writer.WriteValueSafe((int)value.Length); + foreach (var item in value) + { + writer.WriteValueSafe(value); + } + } + /// /// Get the required size to write a NetworkObject /// @@ -217,6 +240,21 @@ public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject writer.WriteValue(value.NetworkObjectId); } + /// + /// Write an array of NetworkObjects + /// + /// The value to write + /// + /// + public static void WriteValue(this ref FastBufferWriter writer, NetworkObject[] value) + { + writer.WriteValue((int)value.Length); + foreach (var item in value) + { + writer.WriteValue(value); + } + } + /// /// Write a NetworkObject /// @@ -224,7 +262,7 @@ public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject /// for multiple writes at once by calling TryBeginWrite. /// /// The value to write - public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObject value) + public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkObject value) { if (!value.IsSpawned) { @@ -233,6 +271,24 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObjec writer.WriteValueSafe(value.NetworkObjectId); } + /// + /// Write an array of NetworkObjects + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// + /// + public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObject[] value) + { + writer.WriteValueSafe((int)value.Length); + foreach (var item in value) + { + writer.WriteValueSafe(value); + } + } + /// /// Get the required size to write a NetworkBehaviour /// @@ -258,7 +314,7 @@ public static int GetNetworkBehaviourWriteSize() /// Write a NetworkBehaviour /// /// The value to write - public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour value) + public static void WriteValue(this ref FastBufferWriter writer, in NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { @@ -269,6 +325,21 @@ public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour writer.WriteValue(value.NetworkBehaviourId); } + /// + /// Write an array of NetworkBehaviours + /// + /// The value to write + /// + /// + public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour[] value) + { + writer.WriteValue((int)value.Length); + foreach (var item in value) + { + writer.WriteValue(value); + } + } + /// /// Write a NetworkBehaviour /// @@ -278,7 +349,7 @@ public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour /// The value to write /// /// - public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehaviour value) + public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { @@ -293,5 +364,23 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehav writer.WriteValue(value.NetworkBehaviourId); } + /// + /// Write an array of NetworkBehaviours + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// + /// + public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehaviour[] value) + { + writer.WriteValueSafe((int)value.Length); + foreach (var item in value) + { + writer.WriteValueSafe(value); + } + } + } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index d97954c243..079600300c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -12,7 +12,6 @@ public interface IBufferSerializerImplementation ref FastBufferWriter GetFastBufferWriter(); void SerializeValue(ref object value, Type type, bool isNullable = false); - void SerializeValue(ref INetworkSerializable value); void SerializeValue(ref GameObject value); void SerializeValue(ref NetworkObject value); void SerializeValue(ref NetworkBehaviour value); @@ -24,7 +23,7 @@ public interface IBufferSerializerImplementation // Has to have a different name to avoid conflicting with "where T: unmananged" // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables // So this is provided as an alternative to avoid boxing allocations. - void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; + void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new(); bool PreCheck(int amount); void SerializeValuePreChecked(ref GameObject value); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs index 48ca46d27c..ac028dc14c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs @@ -2,6 +2,6 @@ namespace Unity.Netcode { public interface INetworkSerializable { - void NetworkSerialize(NetworkSerializer serializer); + void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs deleted file mode 100644 index 0d391dd5f9..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using System.IO; -using static Unity.Netcode.Arithmetic; - -namespace Unity.Netcode -{ - /// - /// A buffer that can be used at the bit level - /// - public class NetworkBuffer : Stream - { - private const int k_InitialCapacity = 16; - private const float k_InitialGrowthFactor = 2.0f; - - private byte[] m_Target; - - /// - /// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data. - /// - /// Initial capacity of buffer in bytes. - /// Factor by which buffer should grow when necessary. - public NetworkBuffer(int capacity, float growthFactor) - { - m_Target = new byte[capacity]; - GrowthFactor = growthFactor; - Resizable = true; - } - - /// - /// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data. - /// - /// Factor by which buffer should grow when necessary. - public NetworkBuffer(float growthFactor) : this(k_InitialCapacity, growthFactor) { } - - /// - /// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data. - /// - /// - public NetworkBuffer(int capacity) : this(capacity, k_InitialGrowthFactor) { } - - /// - /// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data. - /// - public NetworkBuffer() : this(k_InitialCapacity, k_InitialGrowthFactor) { } - - /// - /// A buffer that supports writing data smaller than a single byte. This buffer also has a built-in compression algorithm that can (optionally) be used to write compressed data. - /// NOTE: when using a pre-allocated buffer, the buffer will not grow! - /// - /// Pre-allocated buffer to write to - public NetworkBuffer(byte[] target) - { - m_Target = target; - Resizable = false; - BitLength = (ulong)(target.Length << 3); - } - - internal void SetTarget(byte[] target) - { - m_Target = target; - BitLength = (ulong)(target.Length << 3); - Position = 0; - } - - /// - /// Whether or not the buffer will grow the buffer to accomodate more data. - /// - public bool Resizable { get; } - - private float m_GrowthFactor; - - /// - /// Factor by which buffer should grow when necessary. - /// - public float GrowthFactor { set { m_GrowthFactor = value <= 1 ? 1.5f : value; } get { return m_GrowthFactor; } } - - /// - /// Whether or not buffeer supports reading. (Always true) - /// - public override bool CanRead => true; - - /// - /// Whether or not or there is any data to be read from the buffer. - /// - public bool HasDataToRead => Position < Length; - - /// - /// Whether or not seeking is supported by this buffer. (Always true) - /// - public override bool CanSeek => true; - - /// - /// Whether or not this buffer can accept new data. NOTE: this will return true even if only fewer than 8 bits can be written! - /// - public override bool CanWrite => !BitAligned || Position < m_Target.LongLength || Resizable; - - /// - /// Current buffer size. The buffer will not be resized (if possible) until Position is equal to Capacity and an attempt to write data is made. - /// - public long Capacity - { - get => m_Target.LongLength; // Optimized CeilingExact - set - { - if (value < Length) - { - throw new ArgumentOutOfRangeException("New capcity too small!"); - } - - SetCapacity(value); - } - } - - /// - /// The current length of data considered to be "written" to the buffer. - /// - public override long Length { get => Div8Ceil(BitLength); } - - /// - /// The index that will be written to when any call to write data is made to this buffer. - /// - public override long Position { get => (long)(BitPosition >> 3); set => BitPosition = (ulong)value << 3; } - - /// - /// Bit offset into the buffer that new data will be written to. - /// - public ulong BitPosition { get; set; } - - /// - /// Length of data (in bits) that is considered to be written to the buffer. - /// - public ulong BitLength { get; private set; } - - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - public bool BitAligned { get => (BitPosition & 7) == 0; } - - /// - /// Flush buffer. This does nothing since data is written directly to a byte buffer. - /// - public override void Flush() { } // NOP - - /// - /// Grow buffer if possible. According to Max(bufferLength, 1) * growthFactor^Ceil(newContent/Max(bufferLength, 1)) - /// - /// How many new values need to be accomodated (at least). - //private void Grow(long newContent) => SetCapacity(Math.Max(target.LongLength, 1) * (long)Math.Pow(GrowthFactor, CeilingExact(newContent, Math.Max(target.LongLength, 1)))); - /* - private void Grow(long newContent) - { - float grow = newContent / 64; - if (((long)grow) != grow) grow += 1; - SetCapacity((Capacity + 64) * (long)grow); - } - */ - private void Grow(long newContent) - { - long value = newContent + Capacity; - long newCapacity = value; - - if (newCapacity < 256) - { - newCapacity = 256; - } - // We are ok with this overflowing since the next statement will deal - // with the cases where _capacity*2 overflows. - if (newCapacity < Capacity * 2) - { - newCapacity = Capacity * 2; - } - // We want to expand the array up to Array.MaxArrayLengthOneDimensional - // And we want to give the user the value that they asked for - if ((uint)(Capacity * 2) > int.MaxValue) - { - newCapacity = value > int.MaxValue ? value : int.MaxValue; - } - - SetCapacity(newCapacity); - } - - /// - /// Read a misaligned byte. WARNING: If the current BitPosition isn't byte misaligned, - /// avoid using this method as it may cause an IndexOutOfBoundsException in such a case. - /// - /// A byte extracted from up to two separate buffer indices. - private byte ReadByteMisaligned() - { - int mod = (int)(BitPosition & 7); - return (byte)((m_Target[(int)Position] >> mod) | (m_Target[(int)(BitPosition += 8) >> 3] << (8 - mod))); - } - - /// - /// Read an aligned byte from the buffer. It's recommended to not use this when the BitPosition is byte-misaligned. - /// - /// The byte stored at the current Position index - private byte ReadByteAligned() => m_Target[Position++]; - - /// - /// Read a byte as a byte. This is just for internal use so as to minimize casts (cuz they ugly af). - /// - /// - internal byte ReadByteInternal() => BitAligned ? ReadByteAligned() : ReadByteMisaligned(); - - /// - /// Read a byte from the buffer. This takes into account possible byte misalignment. - /// - /// A byte from the buffer or, if a byte can't be read, -1. - public override int ReadByte() => HasDataToRead ? BitAligned ? ReadByteAligned() : ReadByteMisaligned() : -1; - - /// - /// Peeks a byte without advancing the position - /// - /// The peeked byte - public int PeekByte() => - HasDataToRead - ? BitAligned ? m_Target[Position] : - (byte)((m_Target[(int)Position] >> (int)(BitPosition & 7)) | (m_Target[(int)(BitPosition + 8) >> 3] << (8 - (int)(BitPosition & 7)))) - : -1; - - /// - /// Read a single bit from the buffer. - /// - /// A bit in bool format. (True represents 1, False represents 0) - public bool ReadBit() => (m_Target[Position] & (1 << (int)(BitPosition++ & 7))) != 0; - - /// - /// Read a subset of the buffer buffer and write the contents to the supplied buffer. - /// - /// Buffer to copy data to. - /// Offset into the buffer to write data to. - /// How many bytes to attempt to read. - /// Amount of bytes read. - public override int Read(byte[] buffer, int offset, int count) - { - int tLen = Math.Min(count, (int)(Length - Position)); - for (int i = 0; i < tLen; ++i) - { - buffer[offset + i] = ReadByteInternal(); - } - - return tLen; - } - - /// - /// Set position in buffer to read from/write to. - /// - /// Offset from position origin. - /// How to calculate offset. - /// The new position in the buffer that data will be written to. - public override long Seek(long offset, SeekOrigin origin) - { - return (long)(( - BitPosition = - ( - origin == SeekOrigin.Current ? offset > 0 ? Math.Min(BitPosition + ((ulong)offset << 3), (ulong)m_Target.Length << 3) : - (offset ^ SIGN_BIT_64) > Position ? 0UL : - BitPosition - (ulong)((offset ^ SIGN_BIT_64) << 3) : - origin == SeekOrigin.Begin ? (ulong)Math.Max(0, offset) << 3 : - (ulong)Math.Max(m_Target.Length - offset, 0) << 3 - )) >> 3) + (long)((BitPosition & 1UL) | ((BitPosition >> 1) & 1UL) | ((BitPosition >> 2) & 1UL)); - } - - /// - /// Set the capacity of the internal buffer. - /// - /// New capacity of the buffer - private void SetCapacity(long value) - { - if (!Resizable) - { - throw new NotSupportedException("Can't resize non resizable buffer"); // Don't do shit because fuck you (comment by @GabrielTofvesson -TwoTen) - } - - byte[] newTarg = new byte[value]; - long len = Math.Min(value, m_Target.LongLength); - Buffer.BlockCopy(m_Target, 0, newTarg, 0, (int)len); - if (value < m_Target.LongLength) - { - BitPosition = (ulong)value << 3; - } - - m_Target = newTarg; - } - - /// - /// Set length of data considered to be "written" to the buffer. - /// - /// New length of the written data. - public override void SetLength(long value) - { - if (value < 0) - { - throw new IndexOutOfRangeException("Cannot set a negative length!"); - } - - if (value > Capacity) - { - Grow(value - Capacity); - } - - BitLength = (ulong)value << 3; - BitPosition = Math.Min((ulong)value << 3, BitPosition); - } - - /// - /// Write data from the given buffer to the internal buffer. - /// - /// Buffer to write from. - /// Offset in given buffer to start reading from. - /// Amount of bytes to read copy from given buffer to buffer. - public override void Write(byte[] buffer, int offset, int count) - { - // Check bit alignment. If misaligned, each byte written has to be misaligned - if (BitAligned) - { - if (Position + count >= m_Target.Length) - { - Grow(count); - } - - Buffer.BlockCopy(buffer, offset, m_Target, (int)Position, count); - Position += count; - } - else - { - if (Position + count + 1 >= m_Target.Length) - { - Grow(count); - } - - for (int i = 0; i < count; ++i) - { - WriteMisaligned(buffer[offset + i]); - } - } - - if (BitPosition > BitLength) - { - BitLength = BitPosition; - } - } - - /// - /// Write byte value to the internal buffer. - /// - /// The byte value to write. - public override void WriteByte(byte value) - { - // Check bit alignment. If misaligned, each byte written has to be misaligned - if (BitAligned) - { - if (Position + 1 >= m_Target.Length) - { - Grow(1); - } - - m_Target[Position] = value; - Position += 1; - } - else - { - if (Position + 1 + 1 >= m_Target.Length) - { - Grow(1); - } - - WriteMisaligned(value); - } - - if (BitPosition > BitLength) - { - BitLength = BitPosition; - } - } - - /// - /// Write a misaligned byte. NOTE: Using this when the bit position isn't byte-misaligned may cause an IndexOutOfBoundsException! This does not update the current Length of the buffer. - /// - /// Value to write - private void WriteMisaligned(byte value) - { - int off = (int)(BitPosition & 7); - int shift1 = 8 - off; - m_Target[Position + 1] = (byte)((m_Target[Position + 1] & (0xFF << off)) | (value >> shift1)); - m_Target[Position] = (byte)((m_Target[Position] & (0xFF >> shift1)) | (value << off)); - - BitPosition += 8; - } - - - /// - /// Write a byte (in an int format) to the buffer. This does not update the current Length of the buffer. - /// - /// Value to write - private void WriteIntByte(int value) => WriteBytePrivate((byte)value); - - /// - /// Write a byte (in a ulong format) to the buffer. This does not update the current Length of the buffer. - /// - /// Value to write - private void WriteULongByte(ulong byteValue) => WriteBytePrivate((byte)byteValue); - - /// - /// Write a byte to the buffer. This does not update the current Length of the buffer. - /// - /// Value to write - private void WriteBytePrivate(byte value) - { - if (Div8Ceil(BitPosition) == m_Target.LongLength) - { - Grow(1); - } - - if (BitAligned) - { - m_Target[Position] = value; - BitPosition += 8; - } - else - { - WriteMisaligned(value); - } - - UpdateLength(); - } - - /// - /// Write data from the given buffer to the internal buffer. - /// - /// Buffer to write from. - public void Write(byte[] buffer) => Write(buffer, 0, buffer.Length); - - /// - /// Write a single bit to the buffer - /// - /// Value of the bit. True represents 1, False represents 0 - public void WriteBit(bool bit) - { - if (BitAligned && Position == m_Target.Length) - { - Grow(1); - } - - int offset = (int)(BitPosition & 7); - long pos = Position; - ++BitPosition; - m_Target[pos] = (byte)(bit ? (m_Target[pos] & ~(1 << offset)) | (1 << offset) : (m_Target[pos] & ~(1 << offset))); - UpdateLength(); - } - - /// - /// Copy data from another stream - /// - /// Stream to copy from - /// How many bytes to read. Set to value less than one to read until ReadByte returns -1 - public void CopyFrom(Stream s, int count = -1) - { - if (s is NetworkBuffer b) - { - Write(b.m_Target, 0, count < 0 ? (int)b.Length : count); - } - else - { - long currentPosition = s.Position; - s.Position = 0; - - int read; - bool readToEnd = count < 0; - while ((readToEnd || count-- > 0) && (read = s.ReadByte()) != -1) - { - WriteIntByte(read); - } - - UpdateLength(); - - s.Position = currentPosition; - } - } - - /// - /// Copies internal buffer to stream - /// - /// The stream to copy to - /// The maximum amount of bytes to copy. Set to value less than one to copy the full length -#if !NET35 - public new void CopyTo(Stream stream, int count = -1) -#else - public void CopyTo(Stream stream, int count = -1) -#endif - { - stream.Write(m_Target, 0, count < 0 ? (int)Length : count); - } - - /// - /// Copies urnead bytes from the source stream - /// - /// The source stream to copy from - /// The max amount of bytes to copy - public void CopyUnreadFrom(Stream s, int count = -1) - { - long currentPosition = s.Position; - - int read; - bool readToEnd = count < 0; - while ((readToEnd || count-- > 0) && (read = s.ReadByte()) != -1) - { - WriteIntByte(read); - } - - UpdateLength(); - - s.Position = currentPosition; - } - - // TODO: Implement CopyFrom() for NetworkBuffer with bitCount parameter - /// - /// Copys the bits from the provided NetworkBuffer - /// - /// The buffer to copy from - /// The amount of data evel - /// Whether or not to copy at the bit level rather than the byte level - public void CopyFrom(NetworkBuffer buffer, int dataCount, bool copyBits) - { - if (!copyBits) - { - CopyFrom(buffer, dataCount); - } - else - { - ulong count = dataCount < 0 ? buffer.BitLength : (ulong)dataCount; - if (buffer.BitLength < count) - { - throw new IndexOutOfRangeException("Attempted to read more data than is available"); - } - - Write(buffer.GetBuffer(), 0, (int)(count >> 3)); - for (int i = (int)(count & 7); i >= 0; --i) - { - WriteBit(buffer.ReadBit()); - } - } - } - - /// - /// Update length of data considered to be "written" to the buffer. - /// - private void UpdateLength() - { - if (BitPosition > BitLength) - { - BitLength = BitPosition; - } - } - - /// - /// Get the internal buffer being written to by this buffer. - /// - /// - public byte[] GetBuffer() => m_Target; - - /// - /// Creates a copy of the internal buffer. This only contains the used bytes - /// - /// A copy of used bytes in the internal buffer - public byte[] ToArray() - { - byte[] copy = new byte[Length]; - Buffer.BlockCopy(m_Target, 0, copy, 0, (int)Length); - return copy; - } - - /// - /// Writes zeros to fill the last byte - /// - public void PadBuffer() - { - while (!BitAligned) - { - WriteBit(false); - } - } - - /// - /// Reads zeros until the the buffer is byte aligned - /// - public void SkipPadBits() - { - while (!BitAligned) - { - ReadBit(); - } - } - - /// - /// Returns hex encoded version of the buffer - /// - /// Hex encoded version of the buffer - public override string ToString() => BitConverter.ToString(m_Target, 0, (int)Length); - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs.meta deleted file mode 100644 index 5976fd468c..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBuffer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f6242c3ec07a9e5489d3cdd0cb083173 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs deleted file mode 100644 index 98f6af1976..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs +++ /dev/null @@ -1,2296 +0,0 @@ -#define ARRAY_WRITE_PERMISSIVE // Allow attempt to write "packed" byte array (calls WriteByteArray()) -#define ARRAY_RESOLVE_IMPLICIT // Include WriteArray() method with automatic type resolution -#define ARRAY_WRITE_PREMAP // Create a prefixed array diff mapping -#define ARRAY_DIFF_ALLOW_RESIZE // Whether or not to permit writing diffs of differently sized arrays - -using System; -using System.IO; -using System.Text; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// A BinaryReader that can do bit wise manipulation when backed by a NetworkBuffer - /// - public class NetworkReader - { - private Stream m_Source; - private NetworkBuffer m_NetworkSource; - - /// - /// Creates a new NetworkReader backed by a given stream - /// - /// The stream to read from - public NetworkReader(Stream stream) - { - m_Source = stream; - m_NetworkSource = stream as NetworkBuffer; - } - - /// - /// Changes the underlying stream the reader is reading from - /// - /// The stream to read from - public void SetStream(Stream stream) - { - m_Source = stream; - m_NetworkSource = stream as NetworkBuffer; - } - - /// - /// Retrieves the underlying stream the reader is reading from - /// - /// - public Stream GetStream() - { - return m_Source; - } - - /// - /// Reads a single byte - /// - /// The byte read as an integer - public int ReadByte() => m_Source.ReadByte(); - - /// - /// Reads a byte - /// - /// The byte read - public byte ReadByteDirect() => (byte)m_Source.ReadByte(); - - /// - /// Reads a single bit - /// - /// The bit read - public bool ReadBit() - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - return m_NetworkSource.ReadBit(); - } - - /// - /// Reads a single bit - /// - /// The bit read - public bool ReadBool() - { - if (m_NetworkSource == null) - { - return m_Source.ReadByte() != 0; - } - - // return ReadBit(); // old (buggy) - return ReadByte() != 0; // new (hotfix) - } - - /// - /// Skips pad bits and aligns the position to the next byte - /// - public void SkipPadBits() - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - while (!m_NetworkSource.BitAligned) - { - ReadBit(); - } - } - - /// - /// Reads a single boxed object of a given type in a packed format - /// - /// The type to read - /// Returns the boxed read object - public object ReadObjectPacked(Type type) - { - if (type.IsNullable()) - { - bool isNull = ReadBool(); - - if (isNull) - { - return null; - } - } - - if (SerializationManager.TryDeserialize(m_Source, type, out object obj)) - { - return obj; - } - - if (type.IsArray && type.HasElementType) - { - int size = ReadInt32Packed(); - - var array = Array.CreateInstance(type.GetElementType(), size); - - for (int i = 0; i < size; i++) - { - array.SetValue(ReadObjectPacked(type.GetElementType()), i); - } - - return array; - } - - if (type == typeof(byte)) - { - return ReadByteDirect(); - } - - if (type == typeof(sbyte)) - { - return ReadSByte(); - } - - if (type == typeof(ushort)) - { - return ReadUInt16Packed(); - } - - if (type == typeof(short)) - { - return ReadInt16Packed(); - } - - if (type == typeof(int)) - { - return ReadInt32Packed(); - } - - if (type == typeof(uint)) - { - return ReadUInt32Packed(); - } - - if (type == typeof(long)) - { - return ReadInt64Packed(); - } - - if (type == typeof(ulong)) - { - return ReadUInt64Packed(); - } - - if (type == typeof(float)) - { - return ReadSinglePacked(); - } - - if (type == typeof(double)) - { - return ReadDoublePacked(); - } - - if (type == typeof(string)) - { - return ReadStringPacked(); - } - - if (type == typeof(bool)) - { - return ReadBool(); - } - - if (type == typeof(Vector2)) - { - return ReadVector2Packed(); - } - - if (type == typeof(Vector3)) - { - return ReadVector3Packed(); - } - - if (type == typeof(Vector4)) - { - return ReadVector4Packed(); - } - - if (type == typeof(Color)) - { - return ReadColorPacked(); - } - - if (type == typeof(Color32)) - { - return ReadColor32(); - } - - if (type == typeof(Ray)) - { - return ReadRayPacked(); - } - - if (type == typeof(Quaternion)) - { - return ReadRotationPacked(); - } - - if (type == typeof(char)) - { - return ReadCharPacked(); - } - - if (type.IsEnum) - { - return ReadInt32Packed(); - } - - if (type == typeof(GameObject)) - { - ulong networkObjectId = ReadUInt64Packed(); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - return networkObject.gameObject; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(NetworkReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - return null; - } - - if (type == typeof(NetworkObject)) - { - ulong networkObjectId = ReadUInt64Packed(); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - return networkObject; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(NetworkReader)} cannot find the {nameof(NetworkObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - return null; - } - - if (typeof(NetworkBehaviour).IsAssignableFrom(type)) - { - ulong networkObjectId = ReadUInt64Packed(); - ushort behaviourId = ReadUInt16Packed(); - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - return networkObject.GetNetworkBehaviourAtOrderIndex(behaviourId); - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(NetworkReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - return null; - } - - if (typeof(INetworkSerializable).IsAssignableFrom(type)) - { - object instance = Activator.CreateInstance(type); - ((INetworkSerializable)instance).NetworkSerialize(new NetworkSerializer(this)); - return instance; - } - - Type nullableUnderlyingType = Nullable.GetUnderlyingType(type); - - if (nullableUnderlyingType != null && SerializationManager.IsTypeSupported(nullableUnderlyingType)) - { - return ReadObjectPacked(nullableUnderlyingType); - } - - throw new ArgumentException($"{nameof(NetworkReader)} cannot read type {type.Name}"); - } - - /// - /// Read a single-precision floating point value from the stream. - /// - /// The read value - public float ReadSingle() => new UIntFloat { UIntValue = ReadUInt32() }.FloatValue; - - /// - /// Read a double-precision floating point value from the stream. - /// - /// The read value - public double ReadDouble() => new UIntFloat { ULongValue = ReadUInt64() }.DoubleValue; - - /// - /// Read a single-precision floating point value from the stream from a varint - /// - /// The read value - public float ReadSinglePacked() => new UIntFloat { UIntValue = ReadUInt32Packed() }.FloatValue; - - /// - /// Read a double-precision floating point value from the stream as a varint - /// - /// The read value - public double ReadDoublePacked() => new UIntFloat { ULongValue = ReadUInt64Packed() }.DoubleValue; - - /// - /// Read a Vector2 from the stream. - /// - /// The Vector2 read from the stream. - public Vector2 ReadVector2() => new Vector2(ReadSingle(), ReadSingle()); - - /// - /// Read a Vector2 from the stream. - /// - /// The Vector2 read from the stream. - public Vector2 ReadVector2Packed() => new Vector2(ReadSinglePacked(), ReadSinglePacked()); - - /// - /// Read a Vector3 from the stream. - /// - /// The Vector3 read from the stream. - public Vector3 ReadVector3() => new Vector3(ReadSingle(), ReadSingle(), ReadSingle()); - - /// - /// Read a Vector3 from the stream. - /// - /// The Vector3 read from the stream. - public Vector3 ReadVector3Packed() => new Vector3(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked()); - - /// - /// Read a Vector4 from the stream. - /// - /// The Vector4 read from the stream. - public Vector4 ReadVector4() => new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); - - /// - /// Read a Vector4 from the stream. - /// - /// The Vector4 read from the stream. - public Vector4 ReadVector4Packed() => new Vector4(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked()); - - /// - /// Read a Color from the stream. - /// - /// The Color read from the stream. - public Color ReadColor() => new Color(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); - - /// - /// Read a Color from the stream. - /// - /// The Color read from the stream. - public Color ReadColorPacked() => new Color(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked()); - - /// - /// Read a Color32 from the stream. - /// - /// The Color32 read from the stream. - public Color32 ReadColor32() => new Color32((byte)ReadByte(), (byte)ReadByte(), (byte)ReadByte(), (byte)ReadByte()); - - /// - /// Read a Ray from the stream. - /// - /// The Ray read from the stream. - public Ray ReadRay() => new Ray(ReadVector3(), ReadVector3()); - - /// - /// Read a Ray from the stream. - /// - /// The Ray read from the stream. - public Ray ReadRayPacked() => new Ray(ReadVector3Packed(), ReadVector3Packed()); - - /// - /// Read a Ray2D from the stream. - /// - /// The Ray2D read from the stream. - public Ray2D ReadRay2D() => new Ray2D(ReadVector2(), ReadVector2()); - - /// - /// Read a Ray2D from the stream. - /// - /// The Ray2D read from the stream. - public Ray2D ReadRay2DPacked() => new Ray2D(ReadVector2Packed(), ReadVector2Packed()); - - /// - /// Read a single-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue. - /// - /// Minimum value that this value could be - /// Maximum possible value that this could be - /// How many bytes the compressed value occupies. Must be between 1 and 4 (inclusive) - /// The read value - public float ReadRangedSingle(float minValue, float maxValue, int bytes) - { - if (bytes < 1 || bytes > 4) - { - throw new ArgumentOutOfRangeException("Result must occupy between 1 and 4 bytes!"); - } - - uint read = 0; - for (int i = 0; i < bytes; ++i) - { - read |= (uint)ReadByte() << (i << 3); - } - - return ((float)read / ((0x100 * bytes) - 1) * (maxValue - minValue)) + minValue; - } - - /// - /// read a double-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue. - /// - /// Minimum value that this value could be - /// Maximum possible value that this could be - /// How many bytes the compressed value occupies. Must be between 1 and 8 (inclusive) - /// The read value - public double ReadRangedDouble(double minValue, double maxValue, int bytes) - { - if (bytes < 1 || bytes > 8) - { - throw new ArgumentOutOfRangeException("Result must occupy between 1 and 8 bytes!"); - } - - ulong read = 0; - for (int i = 0; i < bytes; ++i) - { - read |= (ulong)ReadByte() << (i << 3); - } - - return ((double)read / ((0x100 * bytes) - 1) * (maxValue - minValue)) + minValue; - } - - /// - /// Reads the rotation from the stream - /// - /// The rotation read from the stream - public Quaternion ReadRotationPacked() - { - float x = ReadSinglePacked(); - float y = ReadSinglePacked(); - float z = ReadSinglePacked(); - - // numerical precision issues can make the remainder very slightly negative. - // In this case, use 0 for w as, otherwise, w would be NaN. - float remainder = 1f - Mathf.Pow(x, 2) - Mathf.Pow(y, 2) - Mathf.Pow(z, 2); - float w = (remainder > 0f) ? Mathf.Sqrt(remainder) : 0.0f; - - return new Quaternion(x, y, z, w); - } - - /// - /// Reads the rotation from the stream - /// - /// The rotation read from the stream - public Quaternion ReadRotation() - { - float x = ReadSingle(); - float y = ReadSingle(); - float z = ReadSingle(); - float w = ReadSingle(); - - return new Quaternion(x, y, z, w); - } - - /// - /// Read a certain amount of bits from the stream. - /// - /// How many bits to read. Minimum 0, maximum 8. - /// The bits that were read - public ulong ReadBits(int bitCount) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (bitCount > 64) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits into a 64-bit value!"); - } - - if (bitCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); - } - - ulong read = 0; - for (int i = 0; i + 8 < bitCount; i += 8) - { - read |= (ulong)ReadByte() << i; - } - - read |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); - return read; - } - - /// - /// Read a certain amount of bits from the stream. - /// - /// How many bits to read. Minimum 0, maximum 64. - /// The bits that were read - public byte ReadByteBits(int bitCount) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (bitCount > 8) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 8 bits into an 8-bit value!"); - } - - if (bitCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); - } - - int result = 0; - var convert = new ByteBool(); - for (int i = 0; i < bitCount; ++i) - { - result |= convert.Collapse(ReadBit()) << i; - } - - return (byte)result; - } - - /// - /// Read a nibble (4 bits) from the stream. - /// - /// Whether or not the nibble should be left-shifted by 4 bits - /// The nibble that was read - public byte ReadNibble(bool asUpper) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - var convert = new ByteBool(); - - byte result = (byte) - ( - convert.Collapse(ReadBit()) | - (convert.Collapse(ReadBit()) << 1) | - (convert.Collapse(ReadBit()) << 2) | - (convert.Collapse(ReadBit()) << 3) - ); - if (asUpper) - { - result <<= 4; - } - - return result; - } - - // Marginally faster than the one that accepts a bool - /// - /// Read a nibble (4 bits) from the stream. - /// - /// The nibble that was read - public byte ReadNibble() - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - var convert = new ByteBool(); - return (byte) - ( - convert.Collapse(ReadBit()) | - (convert.Collapse(ReadBit()) << 1) | - (convert.Collapse(ReadBit()) << 2) | - (convert.Collapse(ReadBit()) << 3) - ); - } - - /// - /// Reads a signed byte - /// - /// Value read from stream. - public sbyte ReadSByte() => (sbyte)ReadByte(); - - /// - /// Read an unsigned short (UInt16) from the stream. - /// - /// Value read from stream. - public ushort ReadUInt16() => (ushort)(ReadByte() | (ReadByte() << 8)); - - /// - /// Read a signed short (Int16) from the stream. - /// - /// Value read from stream. - public short ReadInt16() => (short)ReadUInt16(); - - /// - /// Read a single character from the stream - /// - /// Value read from stream. - public char ReadChar() => (char)ReadUInt16(); - - /// - /// Read an unsigned int (UInt32) from the stream. - /// - /// Value read from stream. - public uint ReadUInt32() => (uint)(ReadByte() | (ReadByte() << 8) | (ReadByte() << 16) | (ReadByte() << 24)); - - /// - /// Read a signed int (Int32) from the stream. - /// - /// Value read from stream. - public int ReadInt32() => (int)ReadUInt32(); - - /// - /// Read an unsigned long (UInt64) from the stream. - /// - /// Value read from stream. - public ulong ReadUInt64() => - ( - ((uint)ReadByte()) | - ((ulong)ReadByte() << 8) | - ((ulong)ReadByte() << 16) | - ((ulong)ReadByte() << 24) | - ((ulong)ReadByte() << 32) | - ((ulong)ReadByte() << 40) | - ((ulong)ReadByte() << 48) | - ((ulong)ReadByte() << 56) - ); - - /// - /// Read a signed long (Int64) from the stream. - /// - /// Value read from stream. - public long ReadInt64() => (long)ReadUInt64(); - - /// - /// Read a ZigZag encoded varint signed short (Int16) from the stream. - /// - /// Decoded un-varinted value. - public short ReadInt16Packed() => (short)Arithmetic.ZigZagDecode(ReadUInt64Packed()); - - /// - /// Read a varint unsigned short (UInt16) from the stream. - /// - /// Un-varinted value. - public ushort ReadUInt16Packed() => (ushort)ReadUInt64Packed(); - - /// - /// Read a varint two-byte character from the stream. - /// - /// Un-varinted value. - public char ReadCharPacked() => (char)ReadUInt16Packed(); - - /// - /// Read a ZigZag encoded varint signed int (Int32) from the stream. - /// - /// Decoded un-varinted value. - public int ReadInt32Packed() => (int)Arithmetic.ZigZagDecode(ReadUInt64Packed()); - - /// - /// Read a varint unsigned int (UInt32) from the stream. - /// - /// Un-varinted value. - public uint ReadUInt32Packed() => (uint)ReadUInt64Packed(); - - /// - /// Read a ZigZag encoded varint signed long(Int64) from the stream. - /// - /// Decoded un-varinted value. - public long ReadInt64Packed() => Arithmetic.ZigZagDecode(ReadUInt64Packed()); - - /// - /// Read a varint unsigned long (UInt64) from the stream. - /// - /// Un-varinted value. - public ulong ReadUInt64Packed() - { - ulong header = ReadByteDirect(); - if (header <= 240) - { - return header; - } - - if (header <= 248) - { - return 240 + ((header - 241) << 8) + ReadByteDirect(); - } - - if (header == 249) - { - return 2288UL + (ulong)(ReadByte() << 8) + ReadByteDirect(); - } - - ulong res = ReadByteDirect() | ((ulong)ReadByteDirect() << 8) | ((ulong)ReadByte() << 16); - int cmp = 2; - int hdr = (int)(header - 247); - while (hdr > ++cmp) - { - res |= (ulong)ReadByte() << (cmp << 3); - } - - return res; - } - - // Read arrays - - /// - /// Read a string from the stream. - /// - /// The string that was read. - /// If set to true one byte chars are used and only ASCII is supported. - public StringBuilder ReadString(bool oneByteChars) => ReadString(null, oneByteChars); - - /// - /// Read a string from the stream. - /// - /// The string that was read. - /// The builder to read the values into or null to use a new builder. - /// If set to true one byte chars are used and only ASCII is supported. - public StringBuilder ReadString(StringBuilder builder = null, bool oneByteChars = false) - { - int expectedLength = (int)ReadUInt32Packed(); - if (builder == null) - { - builder = new StringBuilder(expectedLength); - } - else if (builder.Capacity + builder.Length < expectedLength) - { - builder.Capacity = expectedLength + builder.Length; - } - - for (int i = 0; i < expectedLength; ++i) - { - builder.Insert(i, oneByteChars ? (char)ReadByte() : ReadChar()); - } - - return builder; - } - - /// - /// Read string encoded as a varint from the stream. - /// - /// The string that was read. - /// The builder to read the string into or null to use a new builder - public string ReadStringPacked(StringBuilder builder = null) - { - int expectedLength = (int)ReadUInt32Packed(); - if (builder == null) - { - builder = new StringBuilder(expectedLength); - } - else if (builder.Capacity + builder.Length < expectedLength) - { - builder.Capacity = expectedLength + builder.Length; - } - - for (int i = 0; i < expectedLength; ++i) - { - builder.Insert(i, ReadCharPacked()); - } - - return builder.ToString(); - } - - /// - /// Read string diff from the stream. - /// - /// The string based on the diff and the old version. - /// The version to compare the diff to. - /// If set to true one byte chars are used and only ASCII is supported. - public StringBuilder ReadStringDiff(string compare, bool oneByteChars = false) => ReadStringDiff(null, compare, oneByteChars); - - /// - /// Read string diff from the stream. - /// - /// The string based on the diff and the old version - /// The builder to read the string into or null to use a new builder. - /// The version to compare the diff to. - /// If set to true one byte chars are used and only ASCII is supported. - public StringBuilder ReadStringDiff(StringBuilder builder, string compare, bool oneByteChars = false) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - int expectedLength = (int)ReadUInt32Packed(); - if (builder == null) - { - builder = new StringBuilder(expectedLength); - } - else if (builder.Capacity < expectedLength) - { - builder.Capacity = expectedLength; - } - - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(compare == null ? 0 : Math.Min(expectedLength, compare.Length)); - ulong mapStart; - int compareLength = compare?.Length ?? 0; - for (int i = 0; i < expectedLength; ++i) - { - if (i >= compareLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - builder.Insert(i, oneByteChars ? (char)ReadByte() : ReadChar()); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < compareLength) - { - builder.Insert(i, compare[i]); - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return builder; - } - - /// - /// Read string diff from the stream. - /// - /// The string based on the diff and the old version. - /// The builder containing the current version and that will also be used as the output buffer. - /// If set to true one byte chars will be used and only ASCII will be supported. - public StringBuilder ReadStringDiff(StringBuilder compareAndBuffer, bool oneByteChars = false) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - int expectedLength = (int)ReadUInt32Packed(); - if (compareAndBuffer == null) - { - throw new ArgumentNullException(nameof(compareAndBuffer), "Buffer cannot be null"); - } - - if (compareAndBuffer.Capacity < expectedLength) - { - compareAndBuffer.Capacity = expectedLength; - } - - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)Math.Min(expectedLength, compareAndBuffer.Length); - ulong mapStart; - for (int i = 0; i < expectedLength; ++i) - { - if (i >= compareAndBuffer.Length || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - compareAndBuffer.Remove(i, 1); - compareAndBuffer.Insert(i, oneByteChars ? (char)ReadByte() : ReadChar()); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return compareAndBuffer; - } - - /// - /// Read string diff encoded as varints from the stream. - /// - /// The string based on the diff and the old version. - /// The version to compare the diff to. - public StringBuilder ReadStringPackedDiff(string compare) => ReadStringPackedDiff(null, compare); - - /// - /// Read string diff encoded as varints from the stream. - /// - /// The string based on the diff and the old version - /// The builder to read the string into or null to use a new builder. - /// The version to compare the diff to. - public StringBuilder ReadStringPackedDiff(StringBuilder builder, string compare) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - int expectedLength = (int)ReadUInt32Packed(); - if (builder == null) - { - builder = new StringBuilder(expectedLength); - } - else if (builder.Capacity < expectedLength) - { - builder.Capacity = expectedLength; - } - - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(compare == null ? 0 : Math.Min(expectedLength, compare.Length)); - ulong mapStart; - int compareLength = compare?.Length ?? 0; - for (int i = 0; i < expectedLength; ++i) - { - if (i >= compareLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - builder.Insert(i, ReadCharPacked()); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < compareLength) - { - builder.Insert(i, compare[i]); - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return builder; - } - - /// - /// Read string diff encoded as varints from the stream. - /// - /// The string based on the diff and the old version. - /// The builder containing the current version and that will also be used as the output buffer. - public StringBuilder ReadStringPackedDiff(StringBuilder compareAndBuffer) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - int expectedLength = (int)ReadUInt32Packed(); - if (compareAndBuffer == null) - { - throw new ArgumentNullException(nameof(compareAndBuffer), "Buffer cannot be null"); - } - - if (compareAndBuffer.Capacity < expectedLength) - { - compareAndBuffer.Capacity = expectedLength; - } - - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)Math.Min(expectedLength, compareAndBuffer.Length); - ulong mapStart; - for (int i = 0; i < expectedLength; ++i) - { - if (i >= compareAndBuffer.Length || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - compareAndBuffer.Remove(i, 1); - compareAndBuffer.Insert(i, ReadCharPacked()); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return compareAndBuffer; - } - - /// - /// Read byte array into an optional buffer from the stream. - /// - /// The byte array that has been read. - /// The array to read into. If the array is not large enough or if it's null. A new array is created. - /// The length of the array if it's known. Otherwise -1 - public byte[] ReadByteArray(byte[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new byte[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadByteDirect(); - } - - return readTo; - } - - /// - /// CreateArraySegment - /// Creates an array segment from the size and offset values passed in. - /// If none are passed in, then it creates an array segment of the entire buffer. - /// - /// size to copy - /// offset within the stream buffer to start copying - /// ArraySegment<byte> - public ArraySegment CreateArraySegment(int sizeToCopy = -1, int offset = -1) - { - if (m_NetworkSource != null) - { - //If no offset was passed, used the current position - bool noOffset = offset == -1; - bool noSizeToCopy = sizeToCopy == -1; - - offset = noOffset ? (int)m_NetworkSource.Position : offset; - sizeToCopy = noSizeToCopy && noOffset ? (int)m_NetworkSource.Length : sizeToCopy; - if (sizeToCopy > 0) - { - //Check to make sure we won't be copying beyond our bounds - if ((m_NetworkSource.Length - offset) >= sizeToCopy) - { - return new ArraySegment(m_NetworkSource.GetBuffer(), offset, sizeToCopy); - } - - //If we didn't pass anything in or passed the length of the buffer - if (sizeToCopy == m_NetworkSource.Length) - { - offset = 0; - } - else - { - Debug.LogError($"{nameof(sizeToCopy)} ({sizeToCopy}) exceeds bounds with an {nameof(offset)} of ({offset})! "); - return new ArraySegment(); - } - - //Return the request array segment - return new ArraySegment(m_NetworkSource.GetBuffer(), offset, sizeToCopy); - } - - Debug.LogError($"{nameof(sizeToCopy)} ({sizeToCopy}) is zero or less! "); - } - else - { - Debug.LogError("Reader has no stream assigned to it! "); - } - - return new ArraySegment(); - } - - /// - /// Read byte array diff into an optional buffer from the stream. - /// - /// The byte array created from the diff and original. - /// The buffer containing the old version or null. - /// The length of the array if it's known. Otherwise -1 - public byte[] ReadByteArrayDiff(byte[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - byte[] writeTo = readTo == null || readTo.LongLength != knownLength ? new byte[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadByteDirect(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read short array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public short[] ReadShortArray(short[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new short[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadInt16(); - } - - return readTo; - } - - /// - /// Read short array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public short[] ReadShortArrayPacked(short[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new short[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadInt16Packed(); - } - - return readTo; - } - - /// - /// Read short array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public short[] ReadShortArrayDiff(short[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - short[] writeTo = readTo == null || readTo.LongLength != knownLength ? new short[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadInt16(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read short array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public short[] ReadShortArrayPackedDiff(short[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - short[] writeTo = readTo == null || readTo.LongLength != knownLength ? new short[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - writeTo[i] = ReadInt16Packed(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - - /// - /// Read ushort array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public ushort[] ReadUShortArray(ushort[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new ushort[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadUInt16(); - } - - return readTo; - } - - /// - /// Read ushort array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public ushort[] ReadUShortArrayPacked(ushort[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new ushort[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadUInt16Packed(); - } - - return readTo; - } - - /// - /// Read ushort array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public ushort[] ReadUShortArrayDiff(ushort[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - ushort[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ushort[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadUInt16(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read ushort array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public ushort[] ReadUShortArrayPackedDiff(ushort[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - ushort[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ushort[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - writeTo[i] = ReadUInt16Packed(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - - /// - /// Read int array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public int[] ReadIntArray(int[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new int[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadInt32(); - } - - return readTo; - } - - /// - /// Read int array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public int[] ReadIntArrayPacked(int[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new int[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadInt32Packed(); - } - - return readTo; - } - - /// - /// Read int array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public int[] ReadIntArrayDiff(int[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - int[] writeTo = readTo == null || readTo.LongLength != knownLength ? new int[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadInt32(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read int array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public int[] ReadIntArrayPackedDiff(int[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - int[] writeTo = readTo == null || readTo.LongLength != knownLength ? new int[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - writeTo[i] = ReadInt32Packed(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - - /// - /// Read uint array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public uint[] ReadUIntArray(uint[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new uint[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadUInt32(); - } - - return readTo; - } - - /// - /// Read uint array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public uint[] ReadUIntArrayPacked(uint[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new uint[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadUInt32Packed(); - } - - return readTo; - } - - /// - /// Read uint array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public uint[] ReadUIntArrayDiff(uint[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - uint[] writeTo = readTo == null || readTo.LongLength != knownLength ? new uint[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadUInt32(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read long array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public long[] ReadLongArray(long[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new long[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadInt64(); - } - - return readTo; - } - - /// - /// Read long array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public long[] ReadLongArrayPacked(long[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new long[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadInt64Packed(); - } - - return readTo; - } - - /// - /// Read long array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public long[] ReadLongArrayDiff(long[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - long[] writeTo = readTo == null || readTo.LongLength != knownLength ? new long[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadInt64(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read long array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public long[] ReadLongArrayPackedDiff(long[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - long[] writeTo = readTo == null || readTo.LongLength != knownLength ? new long[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - writeTo[i] = ReadInt64Packed(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - - /// - /// Read ulong array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public ulong[] ReadULongArray(ulong[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new ulong[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadUInt64(); - } - - return readTo; - } - - /// - /// Read ulong array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public ulong[] ReadULongArrayPacked(ulong[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new ulong[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadUInt64Packed(); - } - - return readTo; - } - - /// - /// Read ulong array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public ulong[] ReadULongArrayDiff(ulong[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - ulong[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ulong[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadUInt64(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read ulong array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public ulong[] ReadULongArrayPackedDiff(ulong[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - ulong[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ulong[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - writeTo[i] = ReadUInt64Packed(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - - /// - /// Read float array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public float[] ReadFloatArray(float[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new float[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadSingle(); - } - - return readTo; - } - - /// - /// Read float array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public float[] ReadFloatArrayPacked(float[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new float[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadSinglePacked(); - } - - return readTo; - } - - /// - /// Read float array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public float[] ReadFloatArrayDiff(float[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - float[] writeTo = readTo == null || readTo.LongLength != knownLength ? new float[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadSingle(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read float array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public float[] ReadFloatArrayPackedDiff(float[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - float[] writeTo = readTo == null || readTo.LongLength != knownLength ? new float[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - readTo[i] = ReadSinglePacked(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - - /// - /// Read double array from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public double[] ReadDoubleArray(double[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new double[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadDouble(); - } - - return readTo; - } - - /// - /// Read double array in a packed format from the stream. - /// - /// The array read from the stream. - /// The buffer to read into or null to create a new array - /// The known length or -1 if unknown - public double[] ReadDoubleArrayPacked(double[] readTo = null, long knownLength = -1) - { - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - if (readTo == null || readTo.LongLength != knownLength) - { - readTo = new double[knownLength]; - } - - for (long i = 0; i < knownLength; ++i) - { - readTo[i] = ReadDoublePacked(); - } - - return readTo; - } - - /// - /// Read double array diff from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public double[] ReadDoubleArrayDiff(double[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - double[] writeTo = readTo == null || readTo.LongLength != knownLength ? new double[knownLength] : readTo; - ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong mapStart; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - mapStart = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = dBlockStart; -#endif - // Read datum - writeTo[i] = ReadDouble(); -#if ARRAY_WRITE_PREMAP - dBlockStart = m_NetworkSource.BitPosition; - // Return to mapping section - m_NetworkSource.BitPosition = mapStart; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = dBlockStart; - return writeTo; - } - - /// - /// Read double array diff in a packed format from the stream. - /// - /// The array created from the diff and the current version. - /// The buffer containing the old version or null. - /// The known length or -1 if unknown - public double[] ReadDoubleArrayPackedDiff(double[] readTo = null, long knownLength = -1) - { - if (m_NetworkSource == null) - { - throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (knownLength < 0) - { - knownLength = (long)ReadUInt64Packed(); - } - - double[] writeTo = readTo == null || readTo.LongLength != knownLength ? new double[knownLength] : readTo; - ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); - ulong rset; - long readToLength = readTo?.LongLength ?? 0; - for (long i = 0; i < knownLength; ++i) - { - if (i >= readToLength || ReadBit()) - { -#if ARRAY_WRITE_PREMAP - // Move to data section - rset = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = data; -#endif - // Read datum - writeTo[i] = ReadDoublePacked(); -#if ARRAY_WRITE_PREMAP - // Return to mapping section - data = m_NetworkSource.BitPosition; - m_NetworkSource.BitPosition = rset; -#endif - } - else if (i < readTo.LongLength) - { - writeTo[i] = readTo[i]; - } - } - - m_NetworkSource.BitPosition = data; - return writeTo; - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs.meta deleted file mode 100644 index 697dfef2f4..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkReader.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4b085e35addc96e41a064eba3674887b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs deleted file mode 100644 index 26637e9273..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs +++ /dev/null @@ -1,910 +0,0 @@ -using System; -using UnityEngine; - -namespace Unity.Netcode -{ - public sealed class NetworkSerializer - { - private readonly NetworkReader m_Reader; - private readonly NetworkWriter m_Writer; - - public NetworkReader Reader => m_Reader; - public NetworkWriter Writer => m_Writer; - - public bool IsReading { get; } - - public NetworkSerializer(NetworkReader reader) - { - m_Reader = reader; - IsReading = true; - } - - public NetworkSerializer(NetworkWriter writer) - { - m_Writer = writer; - IsReading = false; - } - - public void Serialize(ref bool value) - { - if (IsReading) - { - value = m_Reader.ReadBool(); - } - else - { - m_Writer.WriteBool(value); - } - } - - public void Serialize(ref char value) - { - if (IsReading) - { - value = m_Reader.ReadCharPacked(); - } - else - { - m_Writer.WriteCharPacked(value); - } - } - - public void Serialize(ref sbyte value) - { - if (IsReading) - { - value = m_Reader.ReadSByte(); - } - else - { - m_Writer.WriteSByte(value); - } - } - - public void Serialize(ref byte value) - { - if (IsReading) - { - value = m_Reader.ReadByteDirect(); - } - else - { - m_Writer.WriteByte(value); - } - } - - public void Serialize(ref short value) - { - if (IsReading) - { - value = m_Reader.ReadInt16Packed(); - } - else - { - m_Writer.WriteInt16Packed(value); - } - } - - public void Serialize(ref ushort value) - { - if (IsReading) - { - value = m_Reader.ReadUInt16Packed(); - } - else - { - m_Writer.WriteUInt16Packed(value); - } - } - - public void Serialize(ref int value) - { - if (IsReading) - { - value = m_Reader.ReadInt32Packed(); - } - else - { - m_Writer.WriteInt32Packed(value); - } - } - - public void Serialize(ref uint value) - { - if (IsReading) - { - value = m_Reader.ReadUInt32Packed(); - } - else - { - m_Writer.WriteUInt32Packed(value); - } - } - - public void Serialize(ref long value) - { - if (IsReading) - { - value = m_Reader.ReadInt64Packed(); - } - else - { - m_Writer.WriteInt64Packed(value); - } - } - - public void Serialize(ref ulong value) - { - if (IsReading) - { - value = m_Reader.ReadUInt64Packed(); - } - else - { - m_Writer.WriteUInt64Packed(value); - } - } - - public void Serialize(ref float value) - { - if (IsReading) - { - value = m_Reader.ReadSinglePacked(); - } - else - { - m_Writer.WriteSinglePacked(value); - } - } - - public void Serialize(ref double value) - { - if (IsReading) - { - value = m_Reader.ReadDoublePacked(); - } - else - { - m_Writer.WriteDoublePacked(value); - } - } - - public void Serialize(ref string value) - { - if (IsReading) - { - var isSet = m_Reader.ReadBool(); - value = isSet ? m_Reader.ReadStringPacked() : null; - } - else - { - var isSet = value != null; - m_Writer.WriteBool(isSet); - if (isSet) - { - m_Writer.WriteStringPacked(value); - } - } - } - - public void Serialize(ref Color value) - { - if (IsReading) - { - value = m_Reader.ReadColorPacked(); - } - else - { - m_Writer.WriteColorPacked(value); - } - } - - public void Serialize(ref Color32 value) - { - if (IsReading) - { - value = m_Reader.ReadColor32(); - } - else - { - m_Writer.WriteColor32(value); - } - } - - public void Serialize(ref Vector2 value) - { - if (IsReading) - { - value = m_Reader.ReadVector2Packed(); - } - else - { - m_Writer.WriteVector2Packed(value); - } - } - - public void Serialize(ref Vector3 value) - { - if (IsReading) - { - value = m_Reader.ReadVector3Packed(); - } - else - { - m_Writer.WriteVector3Packed(value); - } - } - - public void Serialize(ref Vector4 value) - { - if (IsReading) - { - value = m_Reader.ReadVector4Packed(); - } - else - { - m_Writer.WriteVector4Packed(value); - } - } - - public void Serialize(ref Quaternion value) - { - if (IsReading) - { - value = m_Reader.ReadRotationPacked(); - } - else - { - m_Writer.WriteRotationPacked(value); - } - } - - public void Serialize(ref Ray value) - { - if (IsReading) - { - value = m_Reader.ReadRayPacked(); - } - else - { - m_Writer.WriteRayPacked(value); - } - } - - public void Serialize(ref Ray2D value) - { - if (IsReading) - { - value = m_Reader.ReadRay2DPacked(); - } - else - { - m_Writer.WriteRay2DPacked(value); - } - } - - public unsafe void Serialize(ref TEnum value) where TEnum : unmanaged, Enum - { - if (sizeof(TEnum) == sizeof(int)) - { - if (IsReading) - { - int intValue = m_Reader.ReadInt32Packed(); - value = *(TEnum*)&intValue; - } - else - { - TEnum enumValue = value; - m_Writer.WriteInt32Packed(*(int*)&enumValue); - } - } - else if (sizeof(TEnum) == sizeof(byte)) - { - if (IsReading) - { - byte intValue = m_Reader.ReadByteDirect(); - value = *(TEnum*)&intValue; - } - else - { - TEnum enumValue = value; - m_Writer.WriteByte(*(byte*)&enumValue); - } - } - else if (sizeof(TEnum) == sizeof(short)) - { - if (IsReading) - { - short intValue = m_Reader.ReadInt16Packed(); - value = *(TEnum*)&intValue; - } - else - { - TEnum enumValue = value; - m_Writer.WriteInt16Packed(*(short*)&enumValue); - } - } - else if (sizeof(TEnum) == sizeof(long)) - { - if (IsReading) - { - long intValue = m_Reader.ReadInt64Packed(); - value = *(TEnum*)&intValue; - } - else - { - TEnum enumValue = value; - m_Writer.WriteInt64Packed(*(long*)&enumValue); - } - } - else if (IsReading) - { - value = default; - } - } - - public void Serialize(ref bool[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new bool[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadBool(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteBool(array[i]); - } - } - } - - public void Serialize(ref char[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new char[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadCharPacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteCharPacked(array[i]); - } - } - } - - public void Serialize(ref sbyte[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new sbyte[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadSByte(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteSByte(array[i]); - } - } - } - - public void Serialize(ref byte[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new byte[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadByteDirect(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteByte(array[i]); - } - } - } - - public void Serialize(ref short[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new short[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadInt16Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteInt16Packed(array[i]); - } - } - } - - public void Serialize(ref ushort[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new ushort[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadUInt16Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteUInt16Packed(array[i]); - } - } - } - - public void Serialize(ref int[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new int[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadInt32Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteInt32Packed(array[i]); - } - } - } - - public void Serialize(ref uint[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new uint[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadUInt32Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteUInt32Packed(array[i]); - } - } - } - - public void Serialize(ref long[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new long[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadInt64Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteInt64Packed(array[i]); - } - } - } - - public void Serialize(ref ulong[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new ulong[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadUInt64Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteUInt64Packed(array[i]); - } - } - } - - public void Serialize(ref float[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new float[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadSinglePacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteSinglePacked(array[i]); - } - } - } - - public void Serialize(ref double[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new double[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadDoublePacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteDoublePacked(array[i]); - } - } - } - - public void Serialize(ref string[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new string[length] : null; - for (var i = 0; i < length; ++i) - { - var isSet = m_Reader.ReadBool(); - array[i] = isSet ? m_Reader.ReadStringPacked() : null; - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - var isSet = array[i] != null; - m_Writer.WriteBool(isSet); - if (isSet) - { - m_Writer.WriteStringPacked(array[i]); - } - } - } - } - - public void Serialize(ref Color[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Color[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadColorPacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteColorPacked(array[i]); - } - } - } - - public void Serialize(ref Color32[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Color32[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadColor32(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteColor32(array[i]); - } - } - } - - public void Serialize(ref Vector2[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Vector2[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadVector2Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteVector2Packed(array[i]); - } - } - } - - public void Serialize(ref Vector3[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Vector3[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadVector3Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteVector3Packed(array[i]); - } - } - } - - public void Serialize(ref Vector4[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Vector4[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadVector4Packed(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteVector4Packed(array[i]); - } - } - } - - public void Serialize(ref Quaternion[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Quaternion[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadRotationPacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteRotationPacked(array[i]); - } - } - } - - public void Serialize(ref Ray[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Ray[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadRayPacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteRayPacked(array[i]); - } - } - } - - public void Serialize(ref Ray2D[] array) - { - if (IsReading) - { - var length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new Ray2D[length] : null; - for (var i = 0; i < length; ++i) - { - array[i] = m_Reader.ReadRay2DPacked(); - } - } - else - { - var length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - for (var i = 0; i < length; ++i) - { - m_Writer.WriteRay2DPacked(array[i]); - } - } - } - - public unsafe void Serialize(ref TEnum[] array) where TEnum : unmanaged, Enum - { - int length; - if (IsReading) - { - length = m_Reader.ReadInt32Packed(); - array = length > -1 ? new TEnum[length] : null; - } - else - { - length = array?.Length ?? -1; - m_Writer.WriteInt32Packed(length); - } - - if (sizeof(TEnum) == sizeof(int)) - { - if (IsReading) - { - for (var i = 0; i < length; ++i) - { - int intValue = m_Reader.ReadInt32Packed(); - array[i] = *(TEnum*)&intValue; - } - } - else - { - for (var i = 0; i < length; ++i) - { - TEnum enumValue = array[i]; - m_Writer.WriteInt32Packed(*(int*)&enumValue); - } - } - } - else if (sizeof(TEnum) == sizeof(byte)) - { - if (IsReading) - { - for (var i = 0; i < length; ++i) - { - byte intValue = m_Reader.ReadByteDirect(); - array[i] = *(TEnum*)&intValue; - } - } - else - { - for (var i = 0; i < length; ++i) - { - TEnum enumValue = array[i]; - m_Writer.WriteByte(*(byte*)&enumValue); - } - } - } - else if (sizeof(TEnum) == sizeof(short)) - { - if (IsReading) - { - for (var i = 0; i < length; ++i) - { - short intValue = m_Reader.ReadInt16Packed(); - array[i] = *(TEnum*)&intValue; - } - } - else - { - for (var i = 0; i < length; ++i) - { - TEnum enumValue = array[i]; - m_Writer.WriteInt16Packed(*(short*)&enumValue); - } - } - } - else if (sizeof(TEnum) == sizeof(long)) - { - if (IsReading) - { - for (var i = 0; i < length; ++i) - { - long intValue = m_Reader.ReadInt64Packed(); - array[i] = *(TEnum*)&intValue; - } - } - else - { - for (var i = 0; i < length; ++i) - { - TEnum enumValue = array[i]; - m_Writer.WriteInt64Packed(*(long*)&enumValue); - } - } - } - else if (IsReading) - { - array = default; - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs.meta deleted file mode 100644 index 99e6b04031..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkSerializer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 371e8ff5255834ad7a262f2bf6034b21 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs deleted file mode 100644 index e9961fc6d4..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs +++ /dev/null @@ -1,1875 +0,0 @@ -#define ARRAY_WRITE_PERMISSIVE // Allow attempt to write "packed" byte array (calls WriteByteArray()) -#define ARRAY_RESOLVE_IMPLICIT // Include WriteArray() method with automatic type resolution -#define ARRAY_WRITE_PREMAP // Create a prefixed array diff mapping -#define ARRAY_DIFF_ALLOW_RESIZE // Whether or not to permit writing diffs of differently sized arrays - -using System; -using System.Diagnostics; -using System.IO; -using UnityEngine; - -namespace Unity.Netcode -{ - // Improved version of NetworkWriter - /// - /// A BinaryWriter that can do bit wise manipulation when backed by a NetworkBuffer - /// - public class NetworkWriter - { - private Stream m_Sink; - private NetworkBuffer m_NetworkSink; - - /// - /// Creates a new NetworkWriter backed by a given stream - /// - /// The stream to use for writing - public NetworkWriter(Stream stream) - { - m_Sink = stream; - m_NetworkSink = stream as NetworkBuffer; - } - - /// - /// Changes the underlying stream the writer is writing to - /// - /// The stream to write to - public void SetStream(Stream stream) - { - m_Sink = stream; - m_NetworkSink = stream as NetworkBuffer; - } - - internal Stream GetStream() - { - return m_Sink; - } - - /// - /// Writes a boxed object in a packed format - /// - /// The object to write - public void WriteObjectPacked(object value) - { - // Check unitys custom null checks - bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); - - if (isNull || value.GetType().IsNullable()) - { - WriteBool(isNull); - - if (isNull) - { - return; - } - } - - if (SerializationManager.TrySerialize(m_Sink, value)) - { - return; - } - - if (value is Array array) - { - var elementType = value.GetType().GetElementType(); - - if (SerializationManager.IsTypeSupported(elementType)) - { - WriteInt32Packed(array.Length); - - for (int i = 0; i < array.Length; i++) - { - WriteObjectPacked(array.GetValue(i)); - } - - return; - } - } - else if (value is byte) - { - WriteByte((byte)value); - return; - } - else if (value is sbyte) - { - WriteSByte((sbyte)value); - return; - } - else if (value is ushort) - { - WriteUInt16Packed((ushort)value); - return; - } - else if (value is short) - { - WriteInt16Packed((short)value); - return; - } - else if (value is int) - { - WriteInt32Packed((int)value); - return; - } - else if (value is uint) - { - WriteUInt32Packed((uint)value); - return; - } - else if (value is long) - { - WriteInt64Packed((long)value); - return; - } - else if (value is ulong) - { - WriteUInt64Packed((ulong)value); - return; - } - else if (value is float) - { - WriteSinglePacked((float)value); - return; - } - else if (value is double) - { - WriteDoublePacked((double)value); - return; - } - else if (value is string) - { - WriteStringPacked((string)value); - return; - } - else if (value is bool) - { - WriteBool((bool)value); - return; - } - else if (value is Vector2) - { - WriteVector2Packed((Vector2)value); - return; - } - else if (value is Vector3) - { - WriteVector3Packed((Vector3)value); - return; - } - else if (value is Vector4) - { - WriteVector4Packed((Vector4)value); - return; - } - else if (value is Color) - { - WriteColorPacked((Color)value); - return; - } - else if (value is Color32) - { - WriteColor32((Color32)value); - return; - } - else if (value is Ray) - { - WriteRayPacked((Ray)value); - return; - } - else if (value is Quaternion) - { - WriteRotationPacked((Quaternion)value); - return; - } - else if (value is char) - { - WriteCharPacked((char)value); - return; - } - else if (value.GetType().IsEnum) - { - WriteInt32Packed((int)value); - return; - } - else if (value is GameObject) - { - var networkObject = ((GameObject)value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - WriteUInt64Packed(networkObject.NetworkObjectId); - return; - } - else if (value is NetworkObject) - { - if (!((NetworkObject)value).IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); - } - - WriteUInt64Packed(((NetworkObject)value).NetworkObjectId); - return; - } - else if (value is NetworkBehaviour) - { - if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); - } - - WriteUInt64Packed(((NetworkBehaviour)value).NetworkObjectId); - WriteUInt16Packed(((NetworkBehaviour)value).NetworkBehaviourId); - return; - } - else if (value is INetworkSerializable) - { - ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - } - - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Write single-precision floating point value to the stream - /// - /// Value to write - public void WriteSingle(float value) - { - WriteUInt32(new UIntFloat - { - FloatValue = value - }.UIntValue); - } - - /// - /// Write double-precision floating point value to the stream - /// - /// Value to write - public void WriteDouble(double value) - { - WriteUInt64(new UIntFloat - { - DoubleValue = value - }.ULongValue); - } - - /// - /// Write single-precision floating point value to the stream as a varint - /// - /// Value to write - public void WriteSinglePacked(float value) - { - WriteUInt32Packed(new UIntFloat - { - FloatValue = value - }.UIntValue); - } - - /// - /// Write double-precision floating point value to the stream as a varint - /// - /// Value to write - public void WriteDoublePacked(double value) - { - WriteUInt64Packed(new UIntFloat - { - DoubleValue = value - }.ULongValue); - } - - /// - /// Convenience method that writes two non-packed Vector3 from the ray to the stream - /// - /// Ray to write - public void WriteRay(Ray ray) - { - WriteVector3(ray.origin); - WriteVector3(ray.direction); - } - - /// - /// Convenience method that writes two packed Vector3 from the ray to the stream - /// - /// Ray to write - public void WriteRayPacked(Ray ray) - { - WriteVector3Packed(ray.origin); - WriteVector3Packed(ray.direction); - } - - /// - /// Convenience method that writes two non-packed Vector2 from the ray to the stream - /// - /// Ray2D to write - public void WriteRay2D(Ray2D ray2d) - { - WriteVector2(ray2d.origin); - WriteVector2(ray2d.direction); - } - - /// - /// Convenience method that writes two packed Vector2 from the ray to the stream - /// - /// Ray2D to write - public void WriteRay2DPacked(Ray2D ray2d) - { - WriteVector2Packed(ray2d.origin); - WriteVector2Packed(ray2d.direction); - } - - /// - /// Convenience method that writes four non-varint floats from the color to the stream - /// - /// Color to write - public void WriteColor(Color color) - { - WriteSingle(color.r); - WriteSingle(color.g); - WriteSingle(color.b); - WriteSingle(color.a); - } - - /// - /// Convenience method that writes four varint floats from the color to the stream - /// - /// Color to write - public void WriteColorPacked(Color color) - { - WriteSinglePacked(color.r); - WriteSinglePacked(color.g); - WriteSinglePacked(color.b); - WriteSinglePacked(color.a); - } - - /// - /// Convenience method that writes four non-varint floats from the color to the stream - /// - /// Color32 to write - public void WriteColor32(Color32 color32) - { - WriteByte(color32.r); - WriteByte(color32.g); - WriteByte(color32.b); - WriteByte(color32.a); - } - - /// - /// Convenience method that writes two non-varint floats from the vector to the stream - /// - /// Vector to write - public void WriteVector2(Vector2 vector2) - { - WriteSingle(vector2.x); - WriteSingle(vector2.y); - } - - /// - /// Convenience method that writes two varint floats from the vector to the stream - /// - /// Vector to write - public void WriteVector2Packed(Vector2 vector2) - { - WriteSinglePacked(vector2.x); - WriteSinglePacked(vector2.y); - } - - /// - /// Convenience method that writes three non-varint floats from the vector to the stream - /// - /// Vector to write - public void WriteVector3(Vector3 vector3) - { - WriteSingle(vector3.x); - WriteSingle(vector3.y); - WriteSingle(vector3.z); - } - - /// - /// Convenience method that writes three varint floats from the vector to the stream - /// - /// Vector to write - public void WriteVector3Packed(Vector3 vector3) - { - WriteSinglePacked(vector3.x); - WriteSinglePacked(vector3.y); - WriteSinglePacked(vector3.z); - } - - /// - /// Convenience method that writes four non-varint floats from the vector to the stream - /// - /// Vector to write - public void WriteVector4(Vector4 vector4) - { - WriteSingle(vector4.x); - WriteSingle(vector4.y); - WriteSingle(vector4.z); - WriteSingle(vector4.w); - } - - /// - /// Convenience method that writes four varint floats from the vector to the stream - /// - /// Vector to write - public void WriteVector4Packed(Vector4 vector4) - { - WriteSinglePacked(vector4.x); - WriteSinglePacked(vector4.y); - WriteSinglePacked(vector4.z); - WriteSinglePacked(vector4.w); - } - - /// - /// Write a single-precision floating point value to the stream. The value is between (inclusive) the minValue and maxValue. - /// - /// Value to write - /// Minimum value that this value could be - /// Maximum possible value that this could be - /// How many bytes the compressed result should occupy. Must be between 1 and 4 (inclusive) - public void WriteRangedSingle(float value, float minValue, float maxValue, int bytes) - { - if (bytes < 1 || bytes > 4) - { - throw new ArgumentOutOfRangeException("Result must occupy between 1 and 4 bytes!"); - } - - if (value < minValue || value > maxValue) - { - throw new ArgumentOutOfRangeException("Given value does not match the given constraints!"); - } - - uint result = (uint)((value - minValue) / (maxValue - minValue) * ((0x100 * bytes) - 1)); - for (int i = 0; i < bytes; ++i) - { - m_Sink.WriteByte((byte)(result >> (i << 3))); - } - } - - /// - /// Write a double-precision floating point value to the stream. The value is between (inclusive) the minValue and maxValue. - /// - /// Value to write - /// Minimum value that this value could be - /// Maximum possible value that this could be - /// How many bytes the compressed result should occupy. Must be between 1 and 8 (inclusive) - public void WriteRangedDouble(double value, double minValue, double maxValue, int bytes) - { - if (bytes < 1 || bytes > 8) - { - throw new ArgumentOutOfRangeException("Result must occupy between 1 and 8 bytes!"); - } - - if (value < minValue || value > maxValue) - { - throw new ArgumentOutOfRangeException("Given value does not match the given constraints!"); - } - - ulong result = (ulong)((value - minValue) / (maxValue - minValue) * ((0x100 * bytes) - 1)); - for (int i = 0; i < bytes; ++i) - { - WriteByte((byte)(result >> (i << 3))); - } - } - - /// - /// Writes the rotation to the stream. - /// - /// Rotation to write - public void WriteRotationPacked(Quaternion rotation) - { - if (Mathf.Sign(rotation.w) < 0) - { - WriteSinglePacked(-rotation.x); - WriteSinglePacked(-rotation.y); - WriteSinglePacked(-rotation.z); - } - else - { - WriteSinglePacked(rotation.x); - WriteSinglePacked(rotation.y); - WriteSinglePacked(rotation.z); - } - } - - /// - /// Writes the rotation to the stream. - /// - /// Rotation to write - public void WriteRotation(Quaternion rotation) - { - WriteSingle(rotation.x); - WriteSingle(rotation.y); - WriteSingle(rotation.z); - WriteSingle(rotation.w); - } - - /// - /// Writes a single bit - /// - /// - public void WriteBit(bool bit) - { - if (m_NetworkSink == null) - { - throw new InvalidOperationException($"Cannot write bits on a non-{nameof(NetworkBuffer)} stream"); - } - - m_NetworkSink.WriteBit(bit); - } - - /// - /// Writes a bool as a single bit - /// - /// - public void WriteBool(bool value) - { - if (m_NetworkSink == null) - { - m_Sink.WriteByte(value ? (byte)1 : (byte)0); - } - else - { - // WriteBit(value); // old (buggy) - WriteByte(value ? (byte)1 : (byte)0); // new (hotfix) - } - } - - /// - /// Writes pad bits to make the underlying stream aligned - /// - public void WritePadBits() - { - while (!m_NetworkSink.BitAligned) - { - WriteBit(false); - } - } - - /// - /// Write the lower half (lower nibble) of a byte. - /// - /// Value containing nibble to write. - public void WriteNibble(byte value) => WriteBits(value, 4); - - /// - /// Write either the upper or lower nibble of a byte to the stream. - /// - /// Value holding the nibble - /// Whether or not the upper nibble should be written. True to write the four high bits, else writes the four low bits. - public void WriteNibble(byte value, bool upper) => WriteNibble((byte)(value >> (upper ? 4 : 0))); - - /// - /// Write s certain amount of bits to the stream. - /// - /// Value to get bits from. - /// Amount of bits to write - public void WriteBits(ulong value, int bitCount) - { - if (m_NetworkSink == null) - { - throw new InvalidOperationException($"Cannot write bits on a non-{nameof(NetworkBuffer)} stream"); - } - - if (bitCount > 64) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!"); - } - - if (bitCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); - } - - int count = 0; - for (; count + 8 < bitCount; count += 8) - { - m_NetworkSink.WriteByte((byte)(value >> count)); - } - - for (; count < bitCount; ++count) - { - m_NetworkSink.WriteBit((value & (1UL << count)) != 0); - } - } - - - /// - /// Write bits to stream. - /// - /// Value to get bits from. - /// Amount of bits to write. - public void WriteBits(byte value, int bitCount) - { - if (m_NetworkSink == null) - { - throw new InvalidOperationException($"Cannot write bits on a non-{nameof(NetworkBuffer)} stream"); - } - - for (int i = 0; i < bitCount; ++i) - { - m_NetworkSink.WriteBit(((value >> i) & 1) != 0); - } - } - - /// - /// Write a signed byte to the stream. - /// - /// Value to write - public void WriteSByte(sbyte value) => WriteByte((byte)value); - - /// - /// Write a single character to the stream. - /// - /// Character to write - public void WriteChar(char c) => WriteUInt16(c); - - /// - /// Write an unsigned short (UInt16) to the stream. - /// - /// Value to write - public void WriteUInt16(ushort value) - { - m_Sink.WriteByte((byte)value); - m_Sink.WriteByte((byte)(value >> 8)); - } - - /// - /// Write a signed short (Int16) to the stream. - /// - /// Value to write - public void WriteInt16(short value) => WriteUInt16((ushort)value); - - /// - /// Write an unsigned int (UInt32) to the stream. - /// - /// Value to write - public void WriteUInt32(uint value) - { - m_Sink.WriteByte((byte)value); - m_Sink.WriteByte((byte)(value >> 8)); - m_Sink.WriteByte((byte)(value >> 16)); - m_Sink.WriteByte((byte)(value >> 24)); - } - - /// - /// Write a signed int (Int32) to the stream. - /// - /// Value to write - public void WriteInt32(int value) => WriteUInt32((uint)value); - - /// - /// Write an unsigned long (UInt64) to the stream. - /// - /// Value to write - public void WriteUInt64(ulong value) - { - m_Sink.WriteByte((byte)value); - m_Sink.WriteByte((byte)(value >> 8)); - m_Sink.WriteByte((byte)(value >> 16)); - m_Sink.WriteByte((byte)(value >> 24)); - m_Sink.WriteByte((byte)(value >> 32)); - m_Sink.WriteByte((byte)(value >> 40)); - m_Sink.WriteByte((byte)(value >> 48)); - m_Sink.WriteByte((byte)(value >> 56)); - } - - /// - /// Write a signed long (Int64) to the stream. - /// - /// Value to write - public void WriteInt64(long value) => WriteUInt64((ulong)value); - - /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// - /// Value to write - public void WriteInt16Packed(short value) => WriteInt64Packed(value); - - /// - /// Write an unsigned short (UInt16) as a varint to the stream. - /// - /// Value to write - public void WriteUInt16Packed(ushort value) => WriteUInt64Packed(value); - - /// - /// Write a two-byte character as a varint to the stream. - /// - /// Value to write - public void WriteCharPacked(char c) => WriteUInt16Packed(c); - - /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. - /// - /// Value to write - public void WriteInt32Packed(int value) => WriteInt64Packed(value); - - /// - /// Write an unsigned int (UInt32) as a varint to the stream. - /// - /// Value to write - public void WriteUInt32Packed(uint value) => WriteUInt64Packed(value); - - /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. - /// - /// Value to write - public void WriteInt64Packed(long value) => WriteUInt64Packed(Arithmetic.ZigZagEncode(value)); - - /// - /// Write an unsigned long (UInt64) as a varint to the stream. - /// - /// Value to write - public void WriteUInt64Packed(ulong value) - { - if (value <= 240) - { - WriteULongByte(value); - } - else if (value <= 2287) - { - WriteULongByte(((value - 240) >> 8) + 241); - WriteULongByte(value - 240); - } - else if (value <= 67823) - { - WriteULongByte(249); - WriteULongByte((value - 2288) >> 8); - WriteULongByte(value - 2288); - } - else - { - ulong header = 255; - ulong match = 0x00FF_FFFF_FFFF_FFFFUL; - while (value <= match) - { - --header; - match >>= 8; - } - - WriteULongByte(header); - int max = (int)(header - 247); - for (int i = 0; i < max; ++i) - { - WriteULongByte(value >> (i << 3)); - } - } - } - - /// - /// Write a byte (in an int format) to the stream. - /// - /// Value to write - private void WriteIntByte(int value) => WriteByte((byte)value); - - /// - /// Write a byte (in a ulong format) to the stream. - /// - /// Value to write - private void WriteULongByte(ulong byteValue) => WriteByte((byte)byteValue); - - /// - /// Write a byte to the stream. - /// - /// Value to write - public void WriteByte(byte value) - { - m_Sink.WriteByte(value); - } - - // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays - /// - /// Writes a string - /// - /// The string to write - /// Whether or not to use one byte per character. This will only allow ASCII - public void WriteString(string s, bool oneByteChars = false) - { - WriteUInt32Packed((uint)s.Length); - int target = s.Length; - for (int i = 0; i < target; ++i) - { - if (oneByteChars) - { - WriteByte((byte)s[i]); - } - else - { - WriteChar(s[i]); - } - } - } - - /// - /// Writes a string in a packed format - /// - /// - public void WriteStringPacked(string s) - { - WriteUInt32Packed((uint)s.Length); - int target = s.Length; - for (int i = 0; i < target; ++i) - { - WriteCharPacked(s[i]); - } - } - - /// - /// Writes the diff between two strings - /// - /// The new array - /// The previous array to use for diff - /// Whether or not to use single byte chars. This will only allow ASCII characters - public void WriteStringDiff(string write, string compare, bool oneByteChars = false) - { -#if !ARRAY_DIFF_ALLOW_RESIZE - if (write.Length != compare.Length) throw new ArgumentException("Mismatched string lengths"); -#endif - WriteUInt32Packed((uint)write.Length); - - // Premapping - int target; -#if ARRAY_WRITE_PREMAP -#if ARRAY_DIFF_ALLOW_RESIZE - target = Math.Min(write.Length, compare.Length); -#else - target = a1.Length; -#endif - for (int i = 0; i < target; ++i) - { - WriteBit(write[i] != compare[i]); - } -#else - target = write.Length; -#endif - for (int i = 0; i < target; ++i) - { - bool b = write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - if (oneByteChars) - { - WriteByte((byte)write[i]); - } - else - { - WriteChar(write[i]); - } - } - } - } - - /// - /// Writes the diff between two strings in a packed format - /// - /// The new string - /// The previous string to use for diff - public void WriteStringPackedDiff(string write, string compare) - { -#if !ARRAY_DIFF_ALLOW_RESIZE - if (write.Length != compare.Length) throw new ArgumentException("Mismatched string lengths"); -#endif - WriteUInt32Packed((uint)write.Length); - - // Premapping - int target; -#if ARRAY_WRITE_PREMAP -#if ARRAY_DIFF_ALLOW_RESIZE - target = Math.Min(write.Length, compare.Length); -#else - target = a1.Length; -#endif - for (int i = 0; i < target; ++i) - { - WriteBit(write[i] != compare[i]); - } -#else - target = write.Length; -#endif - for (int i = 0; i < target; ++i) - { - bool b = write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteCharPacked(write[i]); - } - } - } - - private void CheckLengths(Array a1, Array a2) { } - - [Conditional("ARRAY_WRITE_PREMAP")] - private void WritePremap(Array a1, Array a2) - { - long target; - target = Math.Min(a1.LongLength, a2.LongLength); - for (long i = 0; i < target; ++i) - { - WriteBit(!a1.GetValue(i).Equals(a2.GetValue(i))); - } - // TODO: Byte-align here - } - - private ulong WriteArraySize(Array a1, Array a2, long length) - { - ulong write = (ulong)(length >= 0 ? length : a1.LongLength); - if (length < 0) - { - if (length > a1.LongLength) - { - throw new IndexOutOfRangeException("Cannot write more data than is available"); - } - - WriteUInt64Packed(write); - } - - return write; - } - - /// - /// Writes a byte array - /// - /// The array to write - /// The amount of elements to write - public void WriteByteArray(byte[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - m_Sink.WriteByte(b[i]); - } - } - - - /// - /// WriteBytes - /// Takes a byte array buffer and writes the bytes into the currently assigned stream at its current position - /// This reduces the iterations required to write (n) bytes by a factor of up to 8x less iterations. - /// for blocks of memory that exceed 8 bytes in size. It also doesn't require passing arrays over the stack. - /// Ex: - /// 256 bytes iterates 32 times vs 256 times ------------------------- 8x less iterations - /// 64 bytes iterates 8 times vs 64 times----------------------------- 8x less iterations - /// 22 bytes iterates 5 times ( 2-Int64 1-Int32 2-Byte) vs 22 times -- 4x less iterations - /// - /// - /// - public void WriteBytes(byte[] buffer, long targetSize, int offset = 0) - { - long largeInt64Blocks = targetSize >> 3; //Divide by 8 - //8 Byte blocks - for (long i = 0; i < largeInt64Blocks; i++) - { - WriteInt64(BitConverter.ToInt64(buffer, offset)); - offset += 8; - } - - long blockOffset = largeInt64Blocks * 8; - long remainder = targetSize - blockOffset; - - //4 byte block - if (remainder >= 4) - { - WriteInt32(BitConverter.ToInt32(buffer, offset)); - offset += 4; - blockOffset += 4; - } - - //Remainder of bytes < 4 - if (targetSize - blockOffset > 0) - { - for (long i = 0; i < (targetSize - blockOffset); i++) - { - WriteByte(buffer[offset + i]); - } - } - } - - - /// - /// ReadAndWrite - /// Uses a NetworkReader to read (targetSize) bytes and will write (targetSize) bytes to current stream. - /// This reduces the iterations required to write (n) bytes by a factor of up to 8x less iterations. - /// for blocks of memory that exceed 8 bytes in size. It also doesn't require passing arrays over the stack. - /// Ex: - /// 256 bytes iterates 32 times vs 256 times ------------------------- 8x less iterations - /// 64 bytes iterates 8 times vs 64 times----------------------------- 8x less iterations - /// 22 bytes iterates 5 times ( 2-Int64 1-Int32 2-Byte) vs 22 times -- 4x less iterations - /// - /// - /// - public void ReadAndWrite(NetworkReader sourceReader, long targetSize) - { - long largeInt64Blocks = targetSize >> 3; //Divide by 8 - - //8 Byte blocks - for (long i = 0; i < largeInt64Blocks; i++) - { - WriteInt64(sourceReader.ReadInt64()); - } - - long offset = largeInt64Blocks * 8; - long remainder = targetSize - offset; - - //4 byte block - if (remainder >= 4) - { - WriteInt32(sourceReader.ReadInt32()); - offset += 4; - } - - //Remainder of bytes < 4 - if (targetSize - offset > 0) - { - for (long i = 0; i < (targetSize - offset); i++) - { - WriteByte(sourceReader.ReadByteDirect()); - } - } - } - - /// - /// Writes the diff between two byte arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteByteArrayDiff(byte[] write, byte[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(b); -#endif - if (b) - { - WriteByte(write[i]); - } - } - } - - /// - /// Writes a short array - /// - /// The array to write - /// The amount of elements to write - public void WriteShortArray(short[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteInt16(b[i]); - } - } - - /// - /// Writes the diff between two short arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteShortArrayDiff(short[] write, short[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteInt16(write[i]); - } - } - } - - /// - /// Writes a ushort array - /// - /// The array to write - /// The amount of elements to write - public void WriteUShortArray(ushort[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteUInt16(b[i]); - } - } - - /// - /// Writes the diff between two ushort arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteUShortArrayDiff(ushort[] write, ushort[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteUInt16(write[i]); - } - } - } - - /// - /// Writes a char array - /// - /// The array to write - /// The amount of elements to write - public void WriteCharArray(char[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteChar(b[i]); - } - } - - /// - /// Writes the diff between two char arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteCharArrayDiff(char[] write, char[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteChar(write[i]); - } - } - } - - /// - /// Writes a int array - /// - /// The array to write - /// The amount of elements to write - public void WriteIntArray(int[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteInt32(b[i]); - } - } - - /// - /// Writes the diff between two int arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteIntArrayDiff(int[] write, int[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteInt32(write[i]); - } - } - } - - /// - /// Writes a uint array - /// - /// The array to write - /// The amount of elements to write - public void WriteUIntArray(uint[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteUInt32(b[i]); - } - } - - /// - /// Writes the diff between two uint arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteUIntArrayDiff(uint[] write, uint[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteUInt32(write[i]); - } - } - } - - /// - /// Writes a long array - /// - /// The array to write - /// The amount of elements to write - public void WriteLongArray(long[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteInt64(b[i]); - } - } - - /// - /// Writes the diff between two long arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteLongArrayDiff(long[] write, long[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteInt64(write[i]); - } - } - } - - /// - /// Writes a ulong array - /// - /// The array to write - /// The amount of elements to write - public void WriteULongArray(ulong[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteUInt64(b[i]); - } - } - - /// - /// Writes the diff between two ulong arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteULongArrayDiff(ulong[] write, ulong[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteUInt64(write[i]); - } - } - } - - /// - /// Writes a float array - /// - /// The array to write - /// The amount of elements to write - public void WriteFloatArray(float[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteSingle(b[i]); - } - } - - /// - /// Writes the diff between two float arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteFloatArrayDiff(float[] write, float[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteSingle(write[i]); - } - } - } - - /// - /// Writes a double array - /// - /// The array to write - /// The amount of elements to write - public void WriteDoubleArray(double[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteDouble(b[i]); - } - } - - /// - /// Writes the diff between two double arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteDoubleArrayDiff(double[] write, double[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteDouble(write[i]); - } - } - } - - - // Packed arrays -#if ARRAY_RESOLVE_IMPLICIT - /// - /// Writes an array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteArrayPacked(Array a, long count = -1) - { - var arrayType = a.GetType(); - - -#if ARRAY_WRITE_PERMISSIVE - if (arrayType == typeof(byte[])) - { - WriteByteArray(a as byte[], count); - } - else -#endif - if (arrayType == typeof(short[])) - { - WriteShortArrayPacked(a as short[], count); - } - else if (arrayType == typeof(ushort[])) - { - WriteUShortArrayPacked(a as ushort[], count); - } - else if (arrayType == typeof(char[])) - { - WriteCharArrayPacked(a as char[], count); - } - else if (arrayType == typeof(int[])) - { - WriteIntArrayPacked(a as int[], count); - } - else if (arrayType == typeof(uint[])) - { - WriteUIntArrayPacked(a as uint[], count); - } - else if (arrayType == typeof(long[])) - { - WriteLongArrayPacked(a as long[], count); - } - else if (arrayType == typeof(ulong[])) - { - WriteULongArrayPacked(a as ulong[], count); - } - else if (arrayType == typeof(float[])) - { - WriteFloatArrayPacked(a as float[], count); - } - else if (arrayType == typeof(double[])) - { - WriteDoubleArrayPacked(a as double[], count); - } - else - { - throw new InvalidDataException("Unknown array type! Please serialize manually!"); - } - } - - /// - /// Writes the diff between two arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteArrayPackedDiff(Array write, Array compare, long count = -1) - { - var arrayType = write.GetType(); - if (arrayType != compare.GetType()) - { - throw new ArrayTypeMismatchException("Cannot write diff of two differing array types"); - } - -#if ARRAY_WRITE_PERMISSIVE - if (arrayType == typeof(byte[])) - { - WriteByteArrayDiff(write as byte[], compare as byte[], count); - } - else -#endif - if (arrayType == typeof(short[])) - { - WriteShortArrayPackedDiff(write as short[], compare as short[], count); - } - else if (arrayType == typeof(ushort[])) - { - WriteUShortArrayPackedDiff(write as ushort[], compare as ushort[], count); - } - else if (arrayType == typeof(char[])) - { - WriteCharArrayPackedDiff(write as char[], compare as char[], count); - } - else if (arrayType == typeof(int[])) - { - WriteIntArrayPackedDiff(write as int[], compare as int[], count); - } - else if (arrayType == typeof(uint[])) - { - WriteUIntArrayPackedDiff(write as uint[], compare as uint[], count); - } - else if (arrayType == typeof(long[])) - { - WriteLongArrayPackedDiff(write as long[], compare as long[], count); - } - else if (arrayType == typeof(ulong[])) - { - WriteULongArrayPackedDiff(write as ulong[], compare as ulong[], count); - } - else if (arrayType == typeof(float[])) - { - WriteFloatArrayPackedDiff(write as float[], compare as float[], count); - } - else if (arrayType == typeof(double[])) - { - WriteDoubleArrayPackedDiff(write as double[], compare as double[], count); - } - else - { - throw new InvalidDataException("Unknown array type! Please serialize manually!"); - } - } -#endif - - /// - /// Writes a short array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteShortArrayPacked(short[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteInt16Packed(b[i]); - } - } - - /// - /// Writes the diff between two short arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteShortArrayPackedDiff(short[] write, short[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteInt16Packed(write[i]); - } - } - } - - /// - /// Writes a ushort array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteUShortArrayPacked(ushort[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteUInt16Packed(b[i]); - } - } - - /// - /// Writes the diff between two ushort arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteUShortArrayPackedDiff(ushort[] write, ushort[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteUInt16Packed(write[i]); - } - } - } - - /// - /// Writes a char array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteCharArrayPacked(char[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteCharPacked(b[i]); - } - } - - /// - /// Writes the diff between two char arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteCharArrayPackedDiff(char[] write, char[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteCharPacked(write[i]); - } - } - } - - /// - /// Writes a int array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteIntArrayPacked(int[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteInt32Packed(b[i]); - } - } - - /// - /// Writes the diff between two int arrays - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteIntArrayPackedDiff(int[] write, int[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteInt32Packed(write[i]); - } - } - } - - /// - /// Writes a uint array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteUIntArrayPacked(uint[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteUInt32Packed(b[i]); - } - } - - /// - /// Writes the diff between two uing arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteUIntArrayPackedDiff(uint[] write, uint[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteUInt32Packed(write[i]); - } - } - } - - /// - /// Writes a long array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteLongArrayPacked(long[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteInt64Packed(b[i]); - } - } - - /// - /// Writes the diff between two long arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteLongArrayPackedDiff(long[] write, long[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteInt64Packed(write[i]); - } - } - } - - /// - /// Writes a ulong array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteULongArrayPacked(ulong[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteUInt64Packed(b[i]); - } - } - - /// - /// Writes the diff between two ulong arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteULongArrayPackedDiff(ulong[] write, ulong[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteUInt64Packed(write[i]); - } - } - } - - /// - /// Writes a float array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteFloatArrayPacked(float[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteSinglePacked(b[i]); - } - } - - /// - /// Writes the diff between two float arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteFloatArrayPackedDiff(float[] write, float[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteSinglePacked(write[i]); - } - } - } - - /// - /// Writes a double array in a packed format - /// - /// The array to write - /// The amount of elements to write - public void WriteDoubleArrayPacked(double[] b, long count = -1) - { - ulong target = WriteArraySize(b, null, count); - for (ulong i = 0; i < target; ++i) - { - WriteDoublePacked(b[i]); - } - } - - /// - /// Writes the diff between two double arrays in a packed format - /// - /// The new array - /// The previous array to use for diff - /// The amount of elements to write - public void WriteDoubleArrayPackedDiff(double[] write, double[] compare, long count = -1) - { - CheckLengths(write, compare); - long target = (long)WriteArraySize(write, compare, count); - WritePremap(write, compare); - for (long i = 0; i < target; ++i) - { - bool b = i >= compare.LongLength || write[i] != compare[i]; -#if !ARRAY_WRITE_PREMAP - WriteBit(!b); -#endif - if (b) - { - WriteDoublePacked(write[i]); - } - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs.meta deleted file mode 100644 index b5407fc007..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkWriter.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d5f796d542353ae43a8462806a5b98aa -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs deleted file mode 100644 index b1cc8186f2..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Unity.Netcode -{ - /// - /// Static class containing PooledNetworkBuffers - /// - public static class NetworkBufferPool - { - private static uint s_CreatedBuffers = 0; - private static Queue s_OverflowBuffers = new Queue(); - private static Queue s_Buffers = new Queue(); - - private const uint k_MaxBitPoolBuffers = 1024; - private const uint k_MaxCreatedDelta = 512; - - - /// - /// Retrieves an expandable PooledNetworkBuffer from the pool - /// - /// An expandable PooledNetworkBuffer - public static PooledNetworkBuffer GetBuffer() - { - if (s_Buffers.Count == 0) - { - if (s_OverflowBuffers.Count > 0) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"Retrieving {nameof(PooledNetworkBuffer)} from overflow pool. Recent burst?"); - } - - object weakBuffer = null; - while (s_OverflowBuffers.Count > 0 && ((weakBuffer = s_OverflowBuffers.Dequeue().Target) == null)) - { - ; - } - - if (weakBuffer != null) - { - var strongBuffer = (PooledNetworkBuffer)weakBuffer; - - strongBuffer.SetLength(0); - strongBuffer.Position = 0; - - return strongBuffer; - } - } - - if (s_CreatedBuffers == k_MaxBitPoolBuffers) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{k_MaxBitPoolBuffers} buffers have been created. Did you forget to dispose?"); - } - } - else if (s_CreatedBuffers < k_MaxBitPoolBuffers) - { - s_CreatedBuffers++; - } - - return new PooledNetworkBuffer(); - } - - PooledNetworkBuffer buffer = s_Buffers.Dequeue(); - buffer.SetLength(0); - buffer.Position = 0; - - return buffer; - } - - /// - /// Puts a PooledNetworkBuffer back into the pool - /// - /// The buffer to put in the pool - public static void PutBackInPool(PooledNetworkBuffer buffer) - { - if (s_Buffers.Count > k_MaxCreatedDelta) - { - // The user just created lots of buffers without returning them in between. - // Buffers are essentially byte array wrappers. This is valuable memory. - // Thus we put this buffer as a weak reference incase of another burst - // But still leave it to GC - if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"Putting {nameof(PooledNetworkBuffer)} into overflow pool. Did you forget to dispose?"); - } - - s_OverflowBuffers.Enqueue(new WeakReference(buffer)); - } - else - { - s_Buffers.Enqueue(buffer); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs.meta deleted file mode 100644 index 3f6106a42c..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkBufferPool.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b12cc234a5c700041bb1f08f8229f676 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs deleted file mode 100644 index bccc477d74..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Unity.Netcode -{ - /// - /// Static class containing PooledNetworkReaders - /// - public static class NetworkReaderPool - { - private static byte s_CreatedReaders = 0; - private static Queue s_Readers = new Queue(); - - /// - /// Retrieves a PooledNetworkReader - /// - /// The stream the reader should read from - /// A PooledNetworkReader - public static PooledNetworkReader GetReader(Stream stream) - { - if (s_Readers.Count == 0) - { - if (s_CreatedReaders == 254) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning("255 readers have been created. Did you forget to dispose?"); - } - } - else if (s_CreatedReaders < 255) - { - s_CreatedReaders++; - } - - return new PooledNetworkReader(stream); - } - - PooledNetworkReader reader = s_Readers.Dequeue(); - reader.SetStream(stream); - - return reader; - } - - /// - /// Puts a PooledNetworkReader back into the pool - /// - /// The reader to put in the pool - public static void PutBackInPool(PooledNetworkReader reader) - { - if (s_Readers.Count < 64) - { - s_Readers.Enqueue(reader); - } - else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"{nameof(NetworkReaderPool)} already has 64 queued. Throwing to GC. Did you forget to dispose?"); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs.meta deleted file mode 100644 index 09eb815b80..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkReaderPool.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 14a958cf00e46084693623b5c90ba657 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs deleted file mode 100644 index ff753f3f03..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Unity.Netcode -{ - /// - /// Static class containing PooledNetworkWriters - /// - public static class NetworkWriterPool - { - private static byte s_CreatedWriters = 0; - private static Queue s_Writers = new Queue(); - - /// - /// Retrieves a PooledNetworkWriter - /// - /// The stream the writer should write to - /// A PooledNetworkWriter - public static PooledNetworkWriter GetWriter(Stream stream) - { - if (s_Writers.Count == 0) - { - if (s_CreatedWriters == 254) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning("255 writers have been created. Did you forget to dispose?"); - } - } - else if (s_CreatedWriters < 255) - { - s_CreatedWriters++; - } - - return new PooledNetworkWriter(stream); - } - - PooledNetworkWriter writer = s_Writers.Dequeue(); - writer.SetStream(stream); - - return writer; - } - - /// - /// Puts a PooledNetworkWriter back into the pool - /// - /// The writer to put in the pool - public static void PutBackInPool(PooledNetworkWriter writer) - { - if (s_Writers.Count < 64) - { - s_Writers.Enqueue(writer); - } - else if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"{nameof(NetworkWriterPool)} already has 64 queued. Throwing to GC. Did you forget to dispose?"); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs.meta deleted file mode 100644 index 1e4d7493fa..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/NetworkWriterPool.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2cec275be55b22c4ba0fe27f74fbe2a9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs deleted file mode 100644 index 1c744e76ae..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Unity.Netcode -{ - /// - /// Disposable NetworkBuffer that returns back to the NetworkBufferPool when disposed - /// - public sealed class PooledNetworkBuffer : NetworkBuffer - { - private bool m_IsDisposed = false; - - internal PooledNetworkBuffer() { } - - /// - /// Gets a PooledNetworkBuffer from the static NetworkBufferPool - /// - /// PooledNetworkBuffer - public static PooledNetworkBuffer Get() - { - var buffer = NetworkBufferPool.GetBuffer(); - buffer.m_IsDisposed = false; - return buffer; - } - - /// - /// Returns the PooledNetworkBuffer into the static NetworkBufferPool - /// Called by Dispose in the parent class implementation. - /// We cannot override Dispose because it's not declared virtual - /// And if we override it via `public new void Dispose()` then it doesn't get called - /// on anything with a static type other than PooledNetworkBuffer, which then results in a leak: - /// - /// Stream buffer = PooledNetworkBuffer.Get(); - /// buffer.Dispose(); - /// - /// ^ Static type is Stream, this calls Stream::Dispose() instead of PooledNetworkBuffer::Dispose() - /// - public override void Close() - { - if (!m_IsDisposed) - { - m_IsDisposed = true; - NetworkBufferPool.PutBackInPool(this); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs.meta deleted file mode 100644 index 6054cbad29..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkBuffer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4b9ca4cb2f1e219419c81cc68ad3b9b1 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs deleted file mode 100644 index 796b6e5072..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.IO; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// Disposable NetworkReader that returns the Reader to the NetworkReaderPool when disposed - /// - public sealed class PooledNetworkReader : NetworkReader, IDisposable - { - private NetworkSerializer m_Serializer; - public NetworkSerializer Serializer => m_Serializer ?? (m_Serializer = new NetworkSerializer(this)); - - private bool m_IsDisposed = false; - - internal PooledNetworkReader(Stream stream) : base(stream) { } - - /// - /// Gets a PooledNetworkReader from the static NetworkReaderPool - /// - /// PooledNetworkReader - public static PooledNetworkReader Get(Stream stream) - { - var reader = NetworkReaderPool.GetReader(stream); - reader.m_IsDisposed = false; - return reader; - } - - /// - /// Returns the PooledNetworkReader into the static NetworkReaderPool - /// - public void Dispose() - { - if (!m_IsDisposed) - { - m_IsDisposed = true; - NetworkReaderPool.PutBackInPool(this); - } - else - { - Debug.LogWarning("Disposing reader that thinks it is already disposed!"); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs.meta deleted file mode 100644 index aaa5a52dd7..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkReader.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 31928243e74f75541b5e5c4ae87149db -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs deleted file mode 100644 index c199f99a39..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.IO; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// Disposable NetworkWriter that returns the Writer to the NetworkWriterPool when disposed - /// - public sealed class PooledNetworkWriter : NetworkWriter, IDisposable - { - private NetworkSerializer m_Serializer; - public NetworkSerializer Serializer => m_Serializer ?? (m_Serializer = new NetworkSerializer(this)); - - private bool m_IsDisposed = false; - - internal PooledNetworkWriter(Stream stream) : base(stream) { } - - /// - /// Gets a PooledNetworkWriter from the static NetworkWriterPool - /// - /// PooledNetworkWriter - public static PooledNetworkWriter Get(Stream stream) - { - var writer = NetworkWriterPool.GetWriter(stream); - writer.m_IsDisposed = false; - return writer; - } - - /// - /// Returns the PooledNetworkWriter into the static NetworkWriterPool - /// - public void Dispose() - { - if (!m_IsDisposed) - { - m_IsDisposed = true; - NetworkWriterPool.PutBackInPool(this); - } - else - { - Debug.LogError("Writer is being disposed but thinks it is already disposed"); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs.meta deleted file mode 100644 index 3a29bf205b..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/Pooled/PooledNetworkWriter.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: fe89760ee0eccbe4dbf1a0e7cbb86a81 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs deleted file mode 100644 index 4d0614b04d..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// Helper class to manage the netcode serialization - /// - public static class SerializationManager - { - private static Dictionary s_FieldCache = new Dictionary(); - private static Dictionary s_CachedExternalSerializers = new Dictionary(); - private static Dictionary s_CachedExternalDeserializers = new Dictionary(); - - /// - /// The delegate used when registering custom deserialization for a type. - /// - /// The stream to read the data required to construct the type. - /// The type to deserialize. - public delegate T CustomDeserializationDelegate(Stream stream); - - /// - /// The delegate used when registering custom serialization for a type. - /// - /// The stream to write data to that is required to reconstruct the type in the deserialization delegate. - /// The instance to serialize to the stream. - /// The type to serialize. - public delegate void CustomSerializationDelegate(Stream stream, T instance); - - // These two are what we use internally. They box the value. - private delegate void BoxedSerializationDelegate(Stream stream, object instance); - - private delegate object BoxedDeserializationDelegate(Stream stream); - - /// - /// Registers a custom serialization and deserialization pair for a object. - /// This is useful for writing objects that are behind the third party wall. Such as .NET types. - /// - /// The delegate to invoke to serialize the type. - /// The delegate to invoke to deserialize the type. - /// The type to register. - public static void RegisterSerializationHandlers(CustomSerializationDelegate onSerialize, CustomDeserializationDelegate onDeserialize) - { - s_CachedExternalSerializers[typeof(T)] = (stream, instance) => onSerialize(stream, (T)instance); - s_CachedExternalDeserializers[typeof(T)] = stream => onDeserialize(stream); - } - - /// - /// Removes a serialization handler that was registered previously for a specific type. - /// This will remove both the serialization and deserialization handler. - /// - /// The type for the serialization handlers to remove. - /// Whether or not either the serialization or deserialization handlers for the type was removed. - public static bool RemoveSerializationHandlers() - { - bool serializationRemoval = s_CachedExternalSerializers.Remove(typeof(T)); - bool deserializationRemoval = s_CachedExternalDeserializers.Remove(typeof(T)); - - return serializationRemoval || deserializationRemoval; - } - - internal static bool TrySerialize(Stream stream, object obj) - { - if (s_CachedExternalSerializers.TryGetValue(obj.GetType(), out BoxedSerializationDelegate serializer)) - { - serializer(stream, obj); - return true; - } - - return false; - } - - internal static bool TryDeserialize(Stream stream, Type type, out object obj) - { - if (s_CachedExternalDeserializers.TryGetValue(type, out BoxedDeserializationDelegate deserializationDelegate)) - { - obj = deserializationDelegate(stream); - return true; - } - - obj = null; - return false; - } - - internal static FieldInfo[] GetFieldsForType(Type type) - { - if (s_FieldCache.TryGetValue(type, out FieldInfo[] value)) - { - return value; - } - - FieldInfo[] fields = type - .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(x => (x.IsPublic || x.GetCustomAttributes(typeof(SerializeField), true).Length > 0) && IsTypeSupported(x.FieldType)) - .OrderBy(x => x.Name, StringComparer.Ordinal).ToArray(); - - s_FieldCache.Add(type, fields); - - return fields; - } - - private static HashSet s_SupportedTypes = new HashSet() - { - typeof(byte), - typeof(byte), - typeof(sbyte), - typeof(ushort), - typeof(short), - typeof(int), - typeof(uint), - typeof(long), - typeof(ulong), - typeof(float), - typeof(double), - typeof(string), - typeof(bool), - typeof(Vector2), - typeof(Vector3), - typeof(Vector4), - typeof(Color), - typeof(Color32), - typeof(Ray), - typeof(Quaternion), - typeof(char), - typeof(GameObject), - typeof(NetworkObject), - typeof(NetworkBehaviour) - }; - - /// - /// Returns if a type is supported for serialization - /// - /// The type to check - /// Whether or not the type is supported - public static bool IsTypeSupported(Type type) - { - return type.IsEnum || s_SupportedTypes.Contains(type) || type.HasInterface(typeof(INetworkSerializable)) || - (s_CachedExternalSerializers.ContainsKey(type) && s_CachedExternalDeserializers.ContainsKey(type)) || - (type.IsArray && type.HasElementType && IsTypeSupported(type.GetElementType())); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs.meta deleted file mode 100644 index dbea7c922b..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6f42870c1eac2da49aee0bc9057687b8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 7c3096dcf3..eb77881669 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -274,7 +274,7 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO } // Ran on both server and client - internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, Stream dataStream, bool readNetworkVariable, bool destroyWithScene) + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene) { if (networkObject == null) { @@ -285,12 +285,34 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo { throw new SpawnStateException("Object is already spawned"); } + + SpawnNetworkObjectLocallyCommon(networkObject, networkId, sceneObject, playerObject, ownerClientId, destroyWithScene); + } - if (readNetworkVariable && NetworkManager.NetworkConfig.EnableNetworkVariable) + // Ran on both server and client + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, + ref FastBufferReader variableData, bool destroyWithScene) + { + if (networkObject == null) + { + throw new ArgumentNullException(nameof(networkObject), "Cannot spawn null object"); + } + + if (networkObject.IsSpawned) + { + throw new SpawnStateException("Object is already spawned"); + } + + if (sceneObject.Metadata.HasNetworkVariables && NetworkManager.NetworkConfig.EnableNetworkVariable) { - networkObject.SetNetworkVariableData(dataStream); + networkObject.SetNetworkVariableData(ref variableData); } + + SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Metadata.NetworkObjectId, sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.IsPlayerObject, sceneObject.Metadata.OwnerClientId, destroyWithScene); + } + private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene) + { if (SpawnedObjects.ContainsKey(networkId)) { Debug.LogWarning($"Trying to spawn {nameof(NetworkObject.NetworkObjectId)} {networkId} that already exists!"); @@ -355,78 +377,6 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo networkObject.InvokeBehaviourNetworkSpawn(); } - // Ran on both server and client - internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, ref FastBufferReader variableData, bool destroyWithScene) - { - if (networkObject == null) - { - throw new ArgumentNullException(nameof(networkObject), "Cannot spawn null object"); - } - - if (networkObject.IsSpawned) - { - throw new SpawnStateException("Object is already spawned"); - } - - if (sceneObject.Metadata.HasNetworkVariables && NetworkManager.NetworkConfig.EnableNetworkVariable) - { - networkObject.SetNetworkVariableData(ref variableData); - } - - if (SpawnedObjects.ContainsKey(sceneObject.Metadata.NetworkObjectId)) - { - Debug.LogWarning($"Trying to spawn {nameof(NetworkObject.NetworkObjectId)} {sceneObject.Metadata.NetworkObjectId} that already exists!"); - return; - } - - networkObject.IsSpawned = true; - - networkObject.IsSceneObject = sceneObject.Metadata.IsSceneObject; - networkObject.NetworkObjectId = sceneObject.Metadata.NetworkObjectId; - - networkObject.DestroyWithScene = sceneObject.Metadata.IsSceneObject || destroyWithScene; - - networkObject.OwnerClientIdInternal = sceneObject.Metadata.OwnerClientId; - networkObject.IsPlayerObject = sceneObject.Metadata.IsPlayerObject; - - SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject); - SpawnedObjectsList.Add(networkObject); - - NetworkManager.NetworkMetrics.TrackNetworkObject(networkObject); - - if (NetworkManager.IsServer) - { - if (sceneObject.Metadata.IsPlayerObject) - { - NetworkManager.ConnectedClients[sceneObject.Metadata.OwnerClientId].PlayerObject = networkObject; - } - else - { - NetworkManager.ConnectedClients[sceneObject.Metadata.OwnerClientId].OwnedObjects.Add(networkObject); - } - } - else if (sceneObject.Metadata.IsPlayerObject && sceneObject.Metadata.OwnerClientId == NetworkManager.LocalClientId) - { - NetworkManager.ConnectedClients[sceneObject.Metadata.OwnerClientId].PlayerObject = networkObject; - } - - if (NetworkManager.IsServer) - { - for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++) - { - if (networkObject.CheckObjectVisibility == null || networkObject.CheckObjectVisibility(NetworkManager.ConnectedClientsList[i].ClientId)) - { - networkObject.Observers.Add(NetworkManager.ConnectedClientsList[i].ClientId); - } - } - } - - networkObject.SetCachedParent(networkObject.transform.parent); - networkObject.ApplyNetworkParenting(); - NetworkObject.CheckOrphanChildren(); - networkObject.InvokeBehaviourNetworkSpawn(); - } - internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject) { if (!NetworkManager.NetworkConfig.UseSnapshotSpawn) @@ -465,52 +415,6 @@ internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject return parentNetworkObject.NetworkObjectId; } - internal void WriteSpawnCallForObject(PooledNetworkWriter writer, ulong clientId, NetworkObject networkObject) - { - writer.WriteBool(networkObject.IsPlayerObject); - writer.WriteUInt64Packed(networkObject.NetworkObjectId); - writer.WriteUInt64Packed(networkObject.OwnerClientId); - - var parent = GetSpawnParentId(networkObject); - if (parent == null) - { - writer.WriteBool(false); - } - else - { - writer.WriteBool(true); - writer.WriteUInt64Packed(parent.Value); - } - - writer.WriteBool(networkObject.IsSceneObject ?? true); - writer.WriteUInt32Packed(networkObject.HostCheckForGlobalObjectIdHashOverride()); - - if (networkObject.IncludeTransformWhenSpawning == null || networkObject.IncludeTransformWhenSpawning(clientId)) - { - writer.WriteBool(true); - writer.WriteSinglePacked(networkObject.transform.position.x); - writer.WriteSinglePacked(networkObject.transform.position.y); - writer.WriteSinglePacked(networkObject.transform.position.z); - - writer.WriteSinglePacked(networkObject.transform.rotation.eulerAngles.x); - writer.WriteSinglePacked(networkObject.transform.rotation.eulerAngles.y); - writer.WriteSinglePacked(networkObject.transform.rotation.eulerAngles.z); - } - else - { - writer.WriteBool(false); - } - - { - var (isReparented, latestParent) = networkObject.GetNetworkParenting(); - NetworkObject.WriteNetworkParenting(writer, isReparented, latestParent); - } - if (NetworkManager.NetworkConfig.EnableNetworkVariable) - { - networkObject.WriteNetworkVariableData(writer.GetStream(), clientId); - } - } - internal void DespawnObject(NetworkObject networkObject, bool destroyObject = false) { if (!networkObject.IsSpawned) @@ -622,7 +526,7 @@ internal void ServerSpawnSceneObjectsOnStartSweep() { if (networkObjects[i].IsSceneObject == null) { - SpawnNetworkObjectLocally(networkObjects[i], GetNetworkObjectId(), true, false, null, null, false, true); + SpawnNetworkObjectLocally(networkObjects[i], GetNetworkObjectId(), true, false, null, true); } } } @@ -695,33 +599,29 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } else { - var messageQueueContainer = NetworkManager.MessageQueueContainer; - if (messageQueueContainer != null) + if (networkObject != null) { - if (networkObject != null) + // As long as we have any remaining clients, then notify of the object being destroy. + if (NetworkManager.ConnectedClientsList.Count > 0) { - // As long as we have any remaining clients, then notify of the object being destroy. - if (NetworkManager.ConnectedClientsList.Count > 0) - { - m_TargetClientIds.Clear(); + m_TargetClientIds.Clear(); - // We keep only the client for which the object is visible - // as the other clients have them already despawned - foreach (var clientId in NetworkManager.ConnectedClientsIds) + // We keep only the client for which the object is visible + // as the other clients have them already despawned + foreach (var clientId in NetworkManager.ConnectedClientsIds) + { + if (networkObject.IsNetworkVisibleTo(clientId)) { - if (networkObject.IsNetworkVisibleTo(clientId)) - { - m_TargetClientIds.Add(clientId); - } + m_TargetClientIds.Add(clientId); } - - var message = new DestroyObjectMessage - { - NetworkObjectId = networkObject.NetworkObjectId - }; - var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, m_TargetClientIds); - NetworkManager.NetworkMetrics.TrackObjectDestroySent(m_TargetClientIds, networkObject.NetworkObjectId, networkObject.name, size); } + + var message = new DestroyObjectMessage + { + NetworkObjectId = networkObject.NetworkObjectId + }; + var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, m_TargetClientIds); + NetworkManager.NetworkMetrics.TrackObjectDestroySent(m_TargetClientIds, networkObject.NetworkObjectId, networkObject.name, size); } } } @@ -747,43 +647,6 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec } } - - /// - /// This will write all client observable NetworkObjects to the 's stream while also - /// adding the client to each 's list only if - /// observable to the client. - /// Maximum number of objects that could theoretically be serialized is 65536 for now - /// - /// the client identifier used to determine if a spawned NetworkObject is observable - /// contains the writer used for serialization - internal void SerializeObservedNetworkObjects(ulong clientId, NetworkWriter writer) - { - var stream = writer.GetStream(); - var headPosition = stream.Position; - var numberOfObjects = (ushort)0; - - // Write our count place holder(must not be packed!) - writer.WriteUInt16(0); - - foreach (var sobj in SpawnedObjectsList) - { - if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(clientId)) - { - sobj.Observers.Add(clientId); - sobj.SerializeSceneObject(writer, clientId); - numberOfObjects++; - } - } - - var tailPosition = stream.Position; - // Reposition to our count position to the head before we wrote our object count - stream.Position = headPosition; - // Write number of NetworkObjects serialized (must not be packed!) - writer.WriteUInt16(numberOfObjects); - // Set our position back to the tail - stream.Position = tailPosition; - } - /// /// Updates all spawned for the specified client /// Note: if the clientId is the server then it is observable to all spawned 's diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs b/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs deleted file mode 100644 index 521a2c715c..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; -using UnityEngine; - -namespace Unity.Netcode.EditorTests -{ - internal class DummyMessageHandler : IInternalMessageHandler - { - public NetworkManager NetworkManager { get; } - - public DummyMessageHandler(NetworkManager networkManager) - { - NetworkManager = networkManager; - } - - public void HandleSceneEvent(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleSceneEvent)); - - public void MessageReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, MessageQueueContainer.MessageType messageType) - { - VerifyCalled(nameof(MessageReceiveQueueItem)); - if (NetworkManager) - { - // To actually process the message we have to add it to the inbound frame queue for the current update stage - // and then process and flush the queue for the current update stage to actually get it to run through - // MessageQueueContainer.ProcessMessage, which is where the actual code handling the message lives. - // That's what will then call back into this for the others. - var messageQueueContainer = NetworkManager.MessageQueueContainer; - messageQueueContainer.AddQueueItemToInboundFrame(messageType, receiveTime, clientId, (NetworkBuffer)stream); - messageQueueContainer.ProcessAndFlushMessageQueue(MessageQueueContainer.MessageQueueProcessingTypes.Receive, NetworkUpdateLoop.UpdateStage); - messageQueueContainer.AdvanceFrameHistory(MessageQueueHistoryFrame.QueueFrameType.Inbound); - } - } - - public void HandleUnnamedMessage(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleUnnamedMessage)); - - public void HandleNamedMessage(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleNamedMessage)); - - public void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream) => VerifyCalled(nameof(HandleAllClientsSwitchSceneCompleted)); - - private void VerifyCalled(string method) - { - Debug.Log(method); - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs.meta deleted file mode 100644 index 384300ede4..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1a90aa73e34ca0b4f9a5f4e3e593c6f3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs index 5c29f2ceba..d039ea6027 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs @@ -10,103 +10,11 @@ public class MessageBatcherTests [Test] public void SendWithThreshold() { - const int k_BatchThreshold = 256; - const int k_QueueItemCount = 128; - - var sendBatcher = new MessageBatcher(); - var sendStreamQueue = new Queue(); - for (int i = 0; i < k_QueueItemCount; ++i) - { - var randomData = Encoding.ASCII.GetBytes(Guid.NewGuid().ToString()); - var queueItem = new MessageFrameItem - { - NetworkId = 123, - ClientNetworkIds = new ulong[] { 123 }, - Delivery = NetworkDelivery.Reliable, - MessageType = i % 2 == 0 ? MessageQueueContainer.MessageType.ServerRpc : MessageQueueContainer.MessageType.ClientRpc, - MessageData = new ArraySegment(randomData, 0, randomData.Length) - }; - sendBatcher.QueueItem( - queueItem.ClientNetworkIds, - queueItem, - k_BatchThreshold, - (networkId, sendStream) => - { - var queueStream = new NetworkBuffer(); - sendStream.Buffer.CopyTo(queueStream); - sendStreamQueue.Enqueue(queueStream); - }); - } - - // batch the rest - sendBatcher.SendItems( /* thresholdBytes = */ 0, - (networkId, sendStream) => - { - var queueStream = new NetworkBuffer(); - sendStream.Buffer.CopyTo(queueStream); - sendStreamQueue.Enqueue(queueStream); - }); - - var recvBatcher = new MessageBatcher(); - var recvItemCounter = 0; - foreach (var recvStream in sendStreamQueue) - { - recvStream.Position = 0; - recvBatcher.ReceiveItems(recvStream, (stream, type, id, time) => ++recvItemCounter, default, default); - } - - Assert.AreEqual(k_QueueItemCount, recvItemCounter); } [Test] public void SendWithoutThreshold() { - const int k_BatchThreshold = 0; - const int k_QueueItemCount = 128; - - var sendBatcher = new MessageBatcher(); - var sendStreamQueue = new Queue(); - for (int i = 0; i < k_QueueItemCount; ++i) - { - var randomData = Encoding.ASCII.GetBytes(Guid.NewGuid().ToString()); - var queueItem = new MessageFrameItem - { - NetworkId = 123, - ClientNetworkIds = new ulong[] { 123 }, - Delivery = NetworkDelivery.Reliable, - MessageType = i % 2 == 0 ? MessageQueueContainer.MessageType.ServerRpc : MessageQueueContainer.MessageType.ClientRpc, - MessageData = new ArraySegment(randomData, 0, randomData.Length) - }; - sendBatcher.QueueItem( - queueItem.ClientNetworkIds, - queueItem, - k_BatchThreshold, - (networkId, sendStream) => - { - var queueStream = new NetworkBuffer(); - sendStream.Buffer.CopyTo(queueStream); - sendStreamQueue.Enqueue(queueStream); - }); - } - - // batch the rest - sendBatcher.SendItems( /* thresholdBytes = */ 0, - (networkId, sendStream) => - { - var queueStream = new NetworkBuffer(); - sendStream.Buffer.CopyTo(queueStream); - sendStreamQueue.Enqueue(queueStream); - }); - - var recvBatcher = new MessageBatcher(); - var recvItemCounter = 0; - foreach (var recvStream in sendStreamQueue) - { - recvStream.Position = 0; - recvBatcher.ReceiveItems(recvStream, (stream, type, id, time) => ++recvItemCounter, default, default); - } - - Assert.AreEqual(k_QueueItemCount, recvItemCounter); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs b/com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs deleted file mode 100644 index a9ff025854..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Unity.Netcode.EditorTests -{ - internal static class MessagePacker - { - internal static NetworkBuffer WrapMessage(MessageQueueContainer.MessageType messageType, NetworkBuffer messageBody, bool useBatching) - { - var outBuffer = PooledNetworkBuffer.Get(); - var outStream = PooledNetworkWriter.Get(outBuffer); - { - if (useBatching) - { - // write the amounts of bytes that are coming up - MessageBatcher.PushLength((int)messageBody.Length, ref outStream); - - // write the message to send - outStream.WriteBytes(messageBody.GetBuffer(), messageBody.Length); - } - outStream.WriteByte((byte)messageType); - outStream.WriteByte((byte)NetworkUpdateLoop.UpdateStage); - } - outStream.Dispose(); - return outBuffer; - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs.meta deleted file mode 100644 index bba14bbdec..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/MessagePacker.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4b721568f27649bdb006cd201500309f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs deleted file mode 100644 index 7930176667..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs +++ /dev/null @@ -1,913 +0,0 @@ -using System; -using NUnit.Framework; -using System.Text; - -namespace Unity.Netcode.EditorTests -{ - public class NetworkBufferTests - { - [Test] - public void TestEmptyStream() - { - var networkBuffer = new NetworkBuffer(new byte[100]); - Assert.That(networkBuffer.Length, Is.EqualTo(100)); - } - - [Test] - public void TestBool() - { - var networkBuffer = new NetworkBuffer(new byte[100]); - networkBuffer.WriteBit(true); - Assert.That(networkBuffer.Length, Is.EqualTo(100)); - } - - [Test] - public void TestSetLength() - { - var networkBuffer = new NetworkBuffer(4); - networkBuffer.SetLength(100); - - Assert.That(networkBuffer.Capacity, Is.GreaterThanOrEqualTo(100)); - } - - [Test] - public void TestSetLength2() - { - var networkBuffer = new NetworkBuffer(4); - - networkBuffer.WriteByte(1); - networkBuffer.WriteByte(1); - networkBuffer.WriteByte(1); - networkBuffer.WriteByte(1); - - networkBuffer.SetLength(0); - - // position should never go beyond length - Assert.That(networkBuffer.Position, Is.EqualTo(0)); - } - - [Test] - public void TestGrow() - { - // stream should not grow when given a buffer - var networkBuffer = new NetworkBuffer(new byte[0]); - var networkWriter = new NetworkWriter(networkBuffer); - Assert.That(() => { networkWriter.WriteInt64(long.MaxValue); }, Throws.TypeOf()); - } - - [Test] - public void TestInOutBool() - { - var buffer = new byte[100]; - - var outNetworkBuffer = new NetworkBuffer(buffer); - outNetworkBuffer.WriteBit(true); - outNetworkBuffer.WriteBit(false); - outNetworkBuffer.WriteBit(true); - - - // the bit should now be stored in the buffer, lets see if it comes out - - var inNetworkBuffer = new NetworkBuffer(buffer); - - Assert.That(inNetworkBuffer.ReadBit(), Is.True); - Assert.That(inNetworkBuffer.ReadBit(), Is.False); - Assert.That(inNetworkBuffer.ReadBit(), Is.True); - } - - - [Test] - public void TestIntOutPacked16Bit() - { - short svalue = -31934; - ushort uvalue = 64893; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt16Packed(svalue); - outNetworkWriter.WriteUInt16Packed(uvalue); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - Assert.That(inNetworkReader.ReadInt16Packed(), Is.EqualTo(svalue)); - Assert.That(inNetworkReader.ReadUInt16Packed(), Is.EqualTo(uvalue)); - } - - - [Test] - public void TestIntOutPacked32Bit() - { - int svalue = -100913642; - uint uvalue = 1467867235; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt32Packed(svalue); - outNetworkWriter.WriteUInt32Packed(uvalue); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - Assert.That(inNetworkReader.ReadInt32Packed(), Is.EqualTo(svalue)); - Assert.That(inNetworkReader.ReadUInt32Packed(), Is.EqualTo(uvalue)); - } - - - [Test] - public void TestInOutPacked64Bit() - { - var buffer = new byte[100]; - - long someNumber = -1469598103934656037; - ulong uNumber = 81246971249124124; - ulong uNumber2 = 2287; - ulong uNumber3 = 235; - - var outNetworkBuffer = new NetworkBuffer(buffer); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt64Packed(someNumber); - outNetworkWriter.WriteUInt64Packed(uNumber); - outNetworkWriter.WriteUInt64Packed(uNumber2); - outNetworkWriter.WriteUInt64Packed(uNumber3); - - // the bit should now be stored in the buffer, lets see if it comes out - - var inNetworkBuffer = new NetworkBuffer(buffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadInt64Packed(), Is.EqualTo(someNumber)); - Assert.That(inNetworkReader.ReadUInt64Packed(), Is.EqualTo(uNumber)); - Assert.That(inNetworkReader.ReadUInt64Packed(), Is.EqualTo(uNumber2)); - Assert.That(inNetworkReader.ReadUInt64Packed(), Is.EqualTo(uNumber3)); - } - - [Test] - public void TestStreamCopy() - { - var inNetworkBuffer = new NetworkBuffer(); - var copyNetworkBuffer = new NetworkBuffer(); - - byte initialValue1 = 56; - byte initialValue2 = 24; - - inNetworkBuffer.WriteByte(initialValue1); - inNetworkBuffer.WriteByte(initialValue2); - - byte copyValue1 = 27; - byte copyValue2 = 100; - - copyNetworkBuffer.WriteByte(copyValue1); - copyNetworkBuffer.WriteByte(copyValue2); - - inNetworkBuffer.CopyFrom(copyNetworkBuffer, 2); - - var outNetworkBuffer = new NetworkBuffer(inNetworkBuffer.ToArray()); - - Assert.That(outNetworkBuffer.ReadByte(), Is.EqualTo(initialValue1)); - Assert.That(outNetworkBuffer.ReadByte(), Is.EqualTo(initialValue2)); - Assert.That(outNetworkBuffer.ReadByte(), Is.EqualTo(copyValue1)); - Assert.That(outNetworkBuffer.ReadByte(), Is.EqualTo(copyValue2)); - } - - [Test] - public void TestToArray() - { - var inNetworkBuffer = new NetworkBuffer(); - inNetworkBuffer.WriteByte(5); - inNetworkBuffer.WriteByte(6); - Assert.That(inNetworkBuffer.ToArray().Length, Is.EqualTo(2)); - } - - - [Test] - public void TestInOutBytes() - { - var buffer = new byte[100]; - byte someNumber = 0xff; - - var outNetworkBuffer = new NetworkBuffer(buffer); - outNetworkBuffer.WriteByte(someNumber); - - var inNetworkBuffer = new NetworkBuffer(buffer); - Assert.That(inNetworkBuffer.ReadByte(), Is.EqualTo(someNumber)); - } - - [Test] - public void TestInOutInt16() - { - var buffer = new byte[100]; - short someNumber = 23223; - - var outNetworkBuffer = new NetworkBuffer(buffer); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt16(someNumber); - - var inNetworkBuffer = new NetworkBuffer(buffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - short result = inNetworkReader.ReadInt16(); - - Assert.That(result, Is.EqualTo(someNumber)); - } - - [Test] - public void TestInOutInt32() - { - var buffer = new byte[100]; - int someNumber = 23234223; - - var outNetworkBuffer = new NetworkBuffer(buffer); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt32(someNumber); - - var inNetworkBuffer = new NetworkBuffer(buffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - int result = inNetworkReader.ReadInt32(); - - Assert.That(result, Is.EqualTo(someNumber)); - } - - [Test] - public void TestInOutInt64() - { - var buffer = new byte[100]; - long someNumber = 4614256656552045848; - - var outNetworkBuffer = new NetworkBuffer(buffer); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt64(someNumber); - - var inNetworkBuffer = new NetworkBuffer(buffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - long result = inNetworkReader.ReadInt64(); - - Assert.That(result, Is.EqualTo(someNumber)); - } - - [Test] - public void TestInOutMultiple() - { - var buffer = new byte[100]; - short someNumber = -12423; - short someNumber2 = 9322; - - var outNetworkBuffer = new NetworkBuffer(buffer); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteInt16(someNumber); - outNetworkWriter.WriteInt16(someNumber2); - - - // the bit should now be stored in the buffer, lets see if it comes out - - var inNetworkBuffer = new NetworkBuffer(buffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - short result = inNetworkReader.ReadInt16(); - short result2 = inNetworkReader.ReadInt16(); - - Assert.That(result, Is.EqualTo(someNumber)); - Assert.That(result2, Is.EqualTo(someNumber2)); - } - - [Test] - public void TestLength() - { - var inNetworkBuffer = new NetworkBuffer(4); - Assert.That(inNetworkBuffer.Length, Is.EqualTo(0)); - inNetworkBuffer.WriteByte(1); - Assert.That(inNetworkBuffer.Length, Is.EqualTo(1)); - inNetworkBuffer.WriteByte(2); - Assert.That(inNetworkBuffer.Length, Is.EqualTo(2)); - inNetworkBuffer.WriteByte(3); - Assert.That(inNetworkBuffer.Length, Is.EqualTo(3)); - inNetworkBuffer.WriteByte(4); - Assert.That(inNetworkBuffer.Length, Is.EqualTo(4)); - } - - [Test] - public void TestCapacityGrowth() - { - var inNetworkBuffer = new NetworkBuffer(4); - Assert.That(inNetworkBuffer.Capacity, Is.EqualTo(4)); - - inNetworkBuffer.WriteByte(1); - inNetworkBuffer.WriteByte(2); - inNetworkBuffer.WriteByte(3); - inNetworkBuffer.WriteByte(4); - inNetworkBuffer.WriteByte(5); - - // buffer should grow and the reported length - // should not waste any space - // note MemoryStream makes a distinction between Length and Capacity - Assert.That(inNetworkBuffer.Length, Is.EqualTo(5)); - Assert.That(inNetworkBuffer.Capacity, Is.GreaterThanOrEqualTo(5)); - } - - [Test] - public void TestWriteSingle() - { - float somenumber = 0.1f; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - - outNetworkWriter.WriteSingle(somenumber); - var outBuffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(outBuffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadSingle(), Is.EqualTo(somenumber)); - } - - [Test] - public void TestWriteDouble() - { - double somenumber = Math.PI; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - - outNetworkWriter.WriteDouble(somenumber); - var outBuffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(outBuffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadDouble(), Is.EqualTo(somenumber)); - } - - [Test] - public void TestRangedSingle() - { - const float rangeMinA = -180; - const float rangeMaxA = 180; - const int rangeBytesA = 2; - - float randFloatValueA = UnityEngine.Random.Range(rangeMinA, rangeMaxA); - - var outNetworkBufferA = new NetworkBuffer(); - var outNetworkWriterA = new NetworkWriter(outNetworkBufferA); - - outNetworkWriterA.WriteRangedSingle(randFloatValueA, rangeMinA, rangeMaxA, rangeBytesA); - outNetworkWriterA.WriteRangedSingle(rangeMinA, rangeMinA, rangeMaxA, rangeBytesA); - outNetworkWriterA.WriteRangedSingle(rangeMaxA, rangeMinA, rangeMaxA, rangeBytesA); - - var rawBufferBytesA = outNetworkBufferA.GetBuffer(); - - var inNetworkBufferA = new NetworkBuffer(rawBufferBytesA); - var inNetworkReaderA = new NetworkReader(inNetworkBufferA); - - Assert.That(Math.Abs(randFloatValueA - inNetworkReaderA.ReadRangedSingle(rangeMinA, rangeMaxA, rangeBytesA)), Is.LessThan(1.6f)); - Assert.That(Math.Abs(rangeMinA - inNetworkReaderA.ReadRangedSingle(rangeMinA, rangeMaxA, rangeBytesA)), Is.LessThan(1.6f)); - Assert.That(Math.Abs(rangeMaxA - inNetworkReaderA.ReadRangedSingle(rangeMinA, rangeMaxA, rangeBytesA)), Is.LessThan(1.6f)); - - - - const float rangeMinB = 0; - const float rangeMaxB = 360; - const int rangeBytesB = 4; - - float randFloatValueB = UnityEngine.Random.Range(rangeMinB, rangeMaxB); - - var outNetworkBufferB = new NetworkBuffer(); - var outNetworkWriterB = new NetworkWriter(outNetworkBufferB); - - outNetworkWriterB.WriteRangedSingle(randFloatValueB, rangeMinB, rangeMaxB, rangeBytesB); - outNetworkWriterB.WriteRangedSingle(rangeMinB, rangeMinB, rangeMaxB, rangeBytesB); - outNetworkWriterB.WriteRangedSingle(rangeMaxB, rangeMinB, rangeMaxB, rangeBytesB); - - var rawBufferBytesB = outNetworkBufferB.GetBuffer(); - - var inNetworkBufferB = new NetworkBuffer(rawBufferBytesB); - var inNetworkReaderB = new NetworkReader(inNetworkBufferB); - - Assert.That(Math.Abs(randFloatValueB - inNetworkReaderB.ReadRangedSingle(rangeMinB, rangeMaxB, rangeBytesB)), Is.LessThan(0.4f)); - Assert.That(Math.Abs(rangeMinB - inNetworkReaderB.ReadRangedSingle(rangeMinB, rangeMaxB, rangeBytesB)), Is.LessThan(0.4f)); - Assert.That(Math.Abs(rangeMaxB - inNetworkReaderB.ReadRangedSingle(rangeMinB, rangeMaxB, rangeBytesB)), Is.LessThan(0.4f)); - } - - [Test] - public void TestRangedDouble() - { - const double rangeMinA = -180; - const double rangeMaxA = 180; - const int rangeBytesA = 2; - - double randDoubleValueA = new Random().NextDouble() * (rangeMaxA - rangeMinA) + rangeMinA; - - var outNetworkBufferA = new NetworkBuffer(); - var outNetworkWriterA = new NetworkWriter(outNetworkBufferA); - - outNetworkWriterA.WriteRangedDouble(randDoubleValueA, rangeMinA, rangeMaxA, rangeBytesA); - outNetworkWriterA.WriteRangedDouble(rangeMinA, rangeMinA, rangeMaxA, rangeBytesA); - outNetworkWriterA.WriteRangedDouble(rangeMaxA, rangeMinA, rangeMaxA, rangeBytesA); - - var rawBufferBytesA = outNetworkBufferA.GetBuffer(); - - var inNetworkBufferA = new NetworkBuffer(rawBufferBytesA); - var inNetworkReaderA = new NetworkReader(inNetworkBufferA); - - Assert.That(Math.Abs(randDoubleValueA - inNetworkReaderA.ReadRangedDouble(rangeMinA, rangeMaxA, rangeBytesA)), Is.LessThan(1.6f)); - Assert.That(Math.Abs(rangeMinA - inNetworkReaderA.ReadRangedDouble(rangeMinA, rangeMaxA, rangeBytesA)), Is.LessThan(1.6f)); - Assert.That(Math.Abs(rangeMaxA - inNetworkReaderA.ReadRangedDouble(rangeMinA, rangeMaxA, rangeBytesA)), Is.LessThan(1.6f)); - - - - const double rangeMinB = 0; - const double rangeMaxB = 360; - const int rangeBytesB = 4; - - double randDoubleValueB = new Random().NextDouble() * (rangeMaxB - rangeMinB) + rangeMinB; - - var outNetworkBufferB = new NetworkBuffer(); - var outNetworkWriterB = new NetworkWriter(outNetworkBufferB); - - outNetworkWriterB.WriteRangedDouble(randDoubleValueB, rangeMinB, rangeMaxB, rangeBytesB); - outNetworkWriterB.WriteRangedDouble(rangeMinB, rangeMinB, rangeMaxB, rangeBytesB); - outNetworkWriterB.WriteRangedDouble(rangeMaxB, rangeMinB, rangeMaxB, rangeBytesB); - - var rawBufferBytesB = outNetworkBufferB.GetBuffer(); - - var inNetworkBufferB = new NetworkBuffer(rawBufferBytesB); - var inNetworkReaderB = new NetworkReader(inNetworkBufferB); - - Assert.That(Math.Abs(randDoubleValueB - inNetworkReaderB.ReadRangedDouble(rangeMinB, rangeMaxB, rangeBytesB)), Is.LessThan(0.4f)); - Assert.That(Math.Abs(rangeMinB - inNetworkReaderB.ReadRangedDouble(rangeMinB, rangeMaxB, rangeBytesB)), Is.LessThan(0.4f)); - Assert.That(Math.Abs(rangeMaxB - inNetworkReaderB.ReadRangedDouble(rangeMinB, rangeMaxB, rangeBytesB)), Is.LessThan(0.4f)); - } - - [Test] - public void TestWritePackedSingle() - { - float somenumber = (float)Math.PI; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - - outNetworkWriter.WriteSinglePacked(somenumber); - var buffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(buffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadSinglePacked(), Is.EqualTo(somenumber)); - } - - [Test] - public void TestWritePackedDouble() - { - double somenumber = Math.PI; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - - outNetworkWriter.WriteDoublePacked(somenumber); - var outBuffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(outBuffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadDoublePacked(), Is.EqualTo(somenumber)); - } - - [Test] - public void TestWriteMisaligned() - { - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteBit(true); - outNetworkWriter.WriteBit(false); - // now the stream is misalligned, lets write some bytes - outNetworkWriter.WriteByte(244); - outNetworkWriter.WriteByte(123); - outNetworkWriter.WriteInt16(-5457); - outNetworkWriter.WriteUInt64(4773753249); - outNetworkWriter.WriteUInt64Packed(5435285812313212); - outNetworkWriter.WriteInt64Packed(-5435285812313212); - outNetworkWriter.WriteBit(true); - outNetworkWriter.WriteByte(1); - outNetworkWriter.WriteByte(0); - - var outBuffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(outBuffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadBit(), Is.True); - Assert.That(inNetworkReader.ReadBit(), Is.False); - Assert.That(inNetworkReader.ReadByte(), Is.EqualTo(244)); - Assert.That(inNetworkReader.ReadByte(), Is.EqualTo(123)); - Assert.That(inNetworkReader.ReadInt16(), Is.EqualTo(-5457)); - Assert.That(inNetworkReader.ReadUInt64(), Is.EqualTo(4773753249)); - Assert.That(inNetworkReader.ReadUInt64Packed(), Is.EqualTo(5435285812313212)); - Assert.That(inNetworkReader.ReadInt64Packed(), Is.EqualTo(-5435285812313212)); - Assert.That(inNetworkReader.ReadBit(), Is.True); - Assert.That(inNetworkReader.ReadByte(), Is.EqualTo(1)); - Assert.That(inNetworkReader.ReadByte(), Is.EqualTo(0)); - } - - [Test] - public void TestBits() - { - ulong somevalue = 0b1100101010011; - - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteBits(somevalue, 5); - - var outBuffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(outBuffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadBits(5), Is.EqualTo(0b10011)); - } - - [Test] - public void TestNibble() - { - byte somevalue = 0b1010011; - - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteNibble(somevalue); - - var outBuffer = outNetworkBuffer.GetBuffer(); - - var inNetworkBuffer = new NetworkBuffer(outBuffer); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - - Assert.That(inNetworkReader.ReadNibble(), Is.EqualTo(0b0011)); - } - - [Test] - public void TestReadWriteMissaligned() - { - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteBit(true); - var writeBuffer = new byte[16] - { - 0, - 5, - 2, - 54, - 192, - 60, - 214, - 65, - 95, - 2, - 43, - 62, - 252, - 190, - 45, - 2 - }; - outNetworkBuffer.Write(writeBuffer); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - Assert.That(inNetworkBuffer.ReadBit(), Is.True); - var readBuffer = new byte[16]; - inNetworkBuffer.Read(readBuffer, 0, 16); - Assert.That(readBuffer, Is.EquivalentTo(writeBuffer)); - } - - [Test] - public void TestArrays() - { - var outByteArray = new byte[] - { - 1, - 2, - 13, - 37, - 69 - }; - var outIntArray = new int[] - { - 1337, - 69420, - 12345, - 0, - 0, - 5 - }; - var outDoubleArray = new double[] - { - 0.02, - 0.06, - 1E40, - 256.0 - }; - - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteByteArray(outByteArray); - outNetworkWriter.WriteIntArray(outIntArray); - outNetworkWriter.WriteDoubleArray(outDoubleArray); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - var inByteArray = inNetworkReader.ReadByteArray(); - var inIntArray = inNetworkReader.ReadIntArray(); - var inDoubleArray = inNetworkReader.ReadDoubleArray(); - - Assert.That(outByteArray, Is.EqualTo(inByteArray)); - Assert.That(outIntArray, Is.EqualTo(inIntArray)); - Assert.That(outDoubleArray, Is.EqualTo(inDoubleArray)); - } - - [Test] - public void TestArraysPacked() - { - var outShortArray = new short[] - { - 1, - 2, - 13, - 37, - 69 - }; - var outIntArray = new int[] - { - 1337, - 69420, - 12345, - 0, - 0, - 5 - }; - var outDoubleArray = new double[] - { - 0.02, - 0.06, - 1E40, - 256.0 - }; - - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteShortArrayPacked(outShortArray); - outNetworkWriter.WriteIntArrayPacked(outIntArray); - outNetworkWriter.WriteDoubleArrayPacked(outDoubleArray); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - var inShortArray = inNetworkReader.ReadShortArrayPacked(); - var inIntArray = inNetworkReader.ReadIntArrayPacked(); - var inDoubleArray = inNetworkReader.ReadDoubleArrayPacked(); - - Assert.That(outShortArray, Is.EqualTo(inShortArray)); - Assert.That(outIntArray, Is.EqualTo(inIntArray)); - Assert.That(outDoubleArray, Is.EqualTo(inDoubleArray)); - } - - [Test] - public void TestArraysDiff() - { - // Values changed test - var byteOutDiffData = new byte[] - { - 1, - 2, - 13, - 29, - 44, - 15 - }; - var byteOutData = new byte[] - { - 1, - 2, - 13, - 37, - 69 - }; - - // No change test - var intOutDiffData = new int[] - { - 1337, - 69420, - 12345, - 0, - 0, - 5 - }; - var intOutData = new int[] - { - 1337, - 69420, - 12345, - 0, - 0, - 5 - }; - - // Array resize test - var doubleOutDiffData = new double[] - { - 0.2, - 6, - 1E39 - }; - var doubleOutData = new double[] - { - 0.02, - 0.06, - 1E40, - 256.0 - }; - - // Serialize - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteByteArrayDiff(byteOutData, byteOutDiffData); - outNetworkWriter.WriteIntArrayDiff(intOutData, intOutDiffData); - outNetworkWriter.WriteDoubleArrayDiff(doubleOutData, doubleOutDiffData); - - // Deserialize - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - var byteInData = inNetworkReader.ReadByteArrayDiff(byteOutDiffData); - var intInData = inNetworkReader.ReadIntArrayDiff(intOutDiffData); - var doubleInData = inNetworkReader.ReadDoubleArrayDiff(doubleOutDiffData); - - // Compare - Assert.That(byteInData, Is.EqualTo(byteOutData)); - Assert.That(intInData, Is.EqualTo(intOutData)); - Assert.That(doubleInData, Is.EqualTo(doubleOutData)); - } - - [Test] - public void TestArraysPackedDiff() - { - // Values changed test - var longOutDiffData = new long[] - { - 1, - 2, - 13, - 29, - 44, - 15 - }; - var longOutData = new long[] - { - 1, - 2, - 13, - 37, - 69 - }; - - // No change test - var intOutDiffData = new int[] - { - 1337, - 69420, - 12345, - 0, - 0, - 5 - }; - var intOutData = new int[] - { - 1337, - 69420, - 12345, - 0, - 0, - 5 - }; - - // Array resize test - var doubleOutDiffData = new double[] - { - 0.2, - 6, - 1E39 - }; - var doubleOutData = new double[] - { - 0.02, - 0.06, - 1E40, - 256.0 - }; - - // Serialize - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteLongArrayPackedDiff(longOutData, longOutDiffData); - outNetworkWriter.WriteIntArrayPackedDiff(intOutData, intOutDiffData); - outNetworkWriter.WriteDoubleArrayPackedDiff(doubleOutData, doubleOutDiffData); - - // Deserialize - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - var longInData = inNetworkReader.ReadLongArrayPackedDiff(longOutDiffData); - var intInData = inNetworkReader.ReadIntArrayPackedDiff(intOutDiffData); - var doubleInData = inNetworkReader.ReadDoubleArrayPackedDiff(doubleOutDiffData); - - // Compare - Assert.That(longInData, Is.EqualTo(longOutData)); - Assert.That(intInData, Is.EqualTo(intOutData)); - Assert.That(doubleInData, Is.EqualTo(doubleOutData)); - } - - [Test] - public void TestString() - { - var testString = "Hello, World"; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteString(testString); - outNetworkWriter.WriteString(testString, true); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - StringBuilder readBuilder = inNetworkReader.ReadString(); - StringBuilder readBuilderSingle = inNetworkReader.ReadString(true); - - Assert.That(readBuilder.ToString(), Is.EqualTo(testString)); - Assert.That(readBuilderSingle.ToString(), Is.EqualTo(testString)); - } - - [Test] - public void TestStringPacked() - { - var testString = "Hello, World"; - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteStringPacked(testString); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - var readString = inNetworkReader.ReadStringPacked(); - - Assert.That(readString, Is.EqualTo(testString)); - } - - [Test] - public void TestStringDiff() - { - var testString = "Hello, World"; // The simulated "new" value of testString - var originalString = "Heyo, World"; // This is what testString supposedly changed *from* - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteStringDiff(testString, originalString); - outNetworkWriter.WriteStringDiff(testString, originalString, true); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - // Read regular diff - StringBuilder readBuilder = inNetworkReader.ReadStringDiff(originalString); - - // Read diff directly to StringBuilder - inNetworkBuffer.BitPosition = 0; - var stringCompare = new StringBuilder(originalString); - inNetworkReader.ReadStringDiff(stringCompare); - - // Read single-byte diff - StringBuilder byteBuilder = inNetworkReader.ReadStringDiff(originalString, true); - - Assert.That(readBuilder.ToString(), Is.EqualTo(testString)); - Assert.That(stringCompare.ToString(), Is.EqualTo(testString)); - Assert.That(byteBuilder.ToString(), Is.EqualTo(testString)); - } - - [Test] - public void TestStringPackedDiff() - { - var testString = "Hello, World"; // The simulated "new" value of testString - var originalString = "Heyo, World"; // This is what testString supposedly changed *from* - var outNetworkBuffer = new NetworkBuffer(); - var outNetworkWriter = new NetworkWriter(outNetworkBuffer); - outNetworkWriter.WriteStringPackedDiff(testString, originalString); - - var inNetworkBuffer = new NetworkBuffer(outNetworkBuffer.GetBuffer()); - var inNetworkReader = new NetworkReader(inNetworkBuffer); - // Read regular diff - StringBuilder readBuilder = inNetworkReader.ReadStringPackedDiff(originalString); - - // Read diff directly to StringBuilder - inNetworkBuffer.BitPosition = 0; - var stringCompare = new StringBuilder(originalString); - inNetworkReader.ReadStringPackedDiff(stringCompare); - - Assert.That(readBuilder.ToString(), Is.EqualTo(testString)); - Assert.That(stringCompare.ToString(), Is.EqualTo(testString)); - } - - [Test] - public void TestSerializationPipelineBool() - { - var buffer = new NetworkBuffer(); - var writer = new NetworkWriter(buffer); - var reader = new NetworkReader(buffer); - - writer.WriteObjectPacked(true); - writer.WriteObjectPacked(false); - - buffer.BitPosition = 0; - - Assert.That(reader.ReadObjectPacked(typeof(bool)), Is.EqualTo(true)); - Assert.That(reader.ReadObjectPacked(typeof(bool)), Is.EqualTo(false)); - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs.meta deleted file mode 100644 index 04162c07fa..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkBufferTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ed2672ae96472b0e487aae6e77522ee7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs index 79d3c6d752..7ae72dbd17 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs @@ -1,105 +1,8 @@ using System; -using NUnit.Framework; using Unity.Netcode.Editor; -using UnityEngine; -using UnityEngine.TestTools; -using Object = UnityEngine.Object; namespace Unity.Netcode.EditorTests { - public class NetworkManagerMessageHandlerTests - { - [Test] - public void MessageHandlerReceivedMessageServerClient() - { - // Init - var gameObject = new GameObject(nameof(MessageHandlerReceivedMessageServerClient)); - var networkManager = gameObject.AddComponent(); - var transport = gameObject.AddComponent(); - - networkManager.NetworkConfig = new NetworkConfig(); - // Set dummy transport that does nothing - networkManager.NetworkConfig.NetworkTransport = transport; - - // Replace the real message handler with a dummy one that just prints a result - networkManager.MessageHandler = new DummyMessageHandler(networkManager); - - // Have to force the update stage to something valid. It starts at Unset. - NetworkUpdateLoop.UpdateStage = NetworkUpdateStage.Update; - - using var inputBuffer = new NetworkBuffer(); - // Start server since pre-message-handler passes IsServer & IsClient checks - networkManager.StartServer(); - - // Disable batching to make the RPCs come straight through - // This has to be done post start - networkManager.MessageQueueContainer.EnableBatchedMessages(false); - - // Should not cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSceneEvent)); - using var messageStream4 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.SceneEvent, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream4.GetBuffer(), 0, (int)messageStream4.Length), 0); - - // Should cause log (server and client) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleUnnamedMessage)); - using var messageStream8 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.UnnamedMessage, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream8.GetBuffer(), 0, (int)messageStream8.Length), 0); - - // Should cause log (server and client) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNamedMessage)); - using var messageStream9 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.NamedMessage, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream9.GetBuffer(), 0, (int)messageStream9.Length), 0); - - // Stop server to trigger full shutdown - networkManager.Shutdown(); - - // Replace the real message handler with a dummy one that just prints a result - networkManager.MessageHandler = new DummyMessageHandler(networkManager); - - // Start client since pre-message-handler passes IsServer & IsClient checks - networkManager.StartClient(); - - // Disable batching to make the RPCs come straight through - // This has to be done post start (and post restart since the queue container is reset) - networkManager.MessageQueueContainer.EnableBatchedMessages(false); - // Should cause log (client only) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleSceneEvent)); - using var messageStream15 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.SceneEvent, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream15.GetBuffer(), 0, (int)messageStream15.Length), 0); - - // Should cause log (server and client) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleUnnamedMessage)); - using var messageStream19 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.UnnamedMessage, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream19.GetBuffer(), 0, (int)messageStream19.Length), 0); - - // Should cause log (server and client) - // Everything should log MessageReceiveQueueItem even if ignored - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.MessageReceiveQueueItem)); - LogAssert.Expect(LogType.Log, nameof(DummyMessageHandler.HandleNamedMessage)); - using var messageStream20 = MessagePacker.WrapMessage(MessageQueueContainer.MessageType.NamedMessage, inputBuffer, networkManager.MessageQueueContainer.IsUsingBatching()); - networkManager.HandleIncomingData(0, new ArraySegment(messageStream20.GetBuffer(), 0, (int)messageStream20.Length), 0); - - // Full cleanup - networkManager.Shutdown(); - - // Ensure no missmatches with expectations - LogAssert.NoUnexpectedReceived(); - - // Cleanup - Object.DestroyImmediate(gameObject); - } - } - // Should probably have one of these for more files? In the future we could use the SIPTransport? [DontShowInTransportDropdown] internal class DummyTransport : NetworkTransport diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs.meta index b455436403..5a9c34f14b 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs.meta +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerMessageHandlerTests.cs.meta @@ -1,3 +1,11 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: 976ca592c7fa4bcb854203dfbadc0ad9 -timeCreated: 1617913395 \ No newline at end of file +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs deleted file mode 100644 index 45e1734372..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs +++ /dev/null @@ -1,1535 +0,0 @@ -using System; -using NUnit.Framework; -using UnityEngine; - -namespace Unity.Netcode.EditorTests -{ - public class NetworkSerializerTests - { - [Test] - public void SerializeUnspawnedNetworkObject() - { - var gameObject = new GameObject(nameof(SerializeUnspawnedNetworkObject)); - var networkObject = gameObject.AddComponent(); - - try - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - outWriter.WriteObjectPacked(networkObject); - - // we expect the exception below - Assert.Fail(); - } - catch (ArgumentException exception) - { - Assert.True(exception.Message.IndexOf("NetworkWriter cannot write NetworkObject types that are not spawned") != -1); - } - } - - [Test] - public void SerializeBool() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - bool outValueA = true; - bool outValueB = false; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - - // deserialize - bool inValueA = default; - bool inValueB = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - } - - [Test] - public void SerializeChar() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - char outValueA = 'U'; - char outValueB = char.MinValue; - char outValueC = char.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - char inValueA = default; - char inValueB = default; - char inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeSbyte() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - sbyte outValueA = -123; - sbyte outValueB = sbyte.MinValue; - sbyte outValueC = sbyte.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - sbyte inValueA = default; - sbyte inValueB = default; - sbyte inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeByte() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - byte outValueA = 123; - byte outValueB = byte.MinValue; - byte outValueC = byte.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - byte inValueA = default; - byte inValueB = default; - byte inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeShort() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - short outValueA = 12345; - short outValueB = short.MinValue; - short outValueC = short.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - short inValueA = default; - short inValueB = default; - short inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeUshort() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - ushort outValueA = 12345; - ushort outValueB = ushort.MinValue; - ushort outValueC = ushort.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - ushort inValueA = default; - ushort inValueB = default; - ushort inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeInt() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - int outValueA = 1234567890; - int outValueB = int.MinValue; - int outValueC = int.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - int inValueA = default; - int inValueB = default; - int inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeUint() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - uint outValueA = 1234567890; - uint outValueB = uint.MinValue; - uint outValueC = uint.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - uint inValueA = default; - uint inValueB = default; - uint inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeLong() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - long outValueA = 9876543210; - long outValueB = long.MinValue; - long outValueC = long.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - long inValueA = default; - long inValueB = default; - long inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeUlong() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - ulong outValueA = 9876543210; - ulong outValueB = ulong.MinValue; - ulong outValueC = ulong.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - ulong inValueA = default; - ulong inValueB = default; - ulong inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeFloat() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - float outValueA = 12345.6789f; - float outValueB = float.MinValue; - float outValueC = float.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - float inValueA = default; - float inValueB = default; - float inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeDouble() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - double outValueA = 12345.6789; - double outValueB = double.MinValue; - double outValueC = double.MaxValue; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - double inValueA = default; - double inValueB = default; - double inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeString() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - string outValueA = Guid.NewGuid().ToString("N"); - string outValueB = string.Empty; - string outValueC = null; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - string inValueA = default; - string inValueB = default; - string inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeColor() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Color outValueA = Color.black; - Color outValueB = Color.white; - Color outValueC = Color.red; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Color inValueA = default; - Color inValueB = default; - Color inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeColor32() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - var outValueA = new Color32(0, 0, 0, byte.MaxValue); - var outValueB = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var outValueC = new Color32(byte.MaxValue, 0, 0, byte.MaxValue); - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Color32 inValueA = default; - Color32 inValueB = default; - Color32 inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeVector2() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Vector2 outValueA = Vector2.up; - Vector2 outValueB = Vector2.negativeInfinity; - Vector2 outValueC = Vector2.positiveInfinity; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Vector2 inValueA = default; - Vector2 inValueB = default; - Vector2 inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeVector3() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Vector3 outValueA = Vector3.forward; - Vector3 outValueB = Vector3.negativeInfinity; - Vector3 outValueC = Vector3.positiveInfinity; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Vector3 inValueA = default; - Vector3 inValueB = default; - Vector3 inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeVector4() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Vector4 outValueA = Vector4.one; - Vector4 outValueB = Vector4.negativeInfinity; - Vector4 outValueC = Vector4.positiveInfinity; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Vector4 inValueA = default; - Vector4 inValueB = default; - Vector4 inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeQuaternion() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Quaternion outValueA = Quaternion.identity; - var outValueB = Quaternion.Euler(new Vector3(30, 45, -60)); - var outValueC = Quaternion.Euler(new Vector3(90, -90, 180)); - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Quaternion inValueA = default; - Quaternion inValueB = default; - Quaternion inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.Greater(Mathf.Abs(Quaternion.Dot(inValueA, outValueA)), 0.999f); - Assert.Greater(Mathf.Abs(Quaternion.Dot(inValueB, outValueB)), 0.999f); - Assert.Greater(Mathf.Abs(Quaternion.Dot(inValueC, outValueC)), 0.999f); - } - - [Test] - public void SerializeRay() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - var outValueA = new Ray(Vector3.zero, Vector3.forward); - var outValueB = new Ray(Vector3.zero, Vector3.left); - var outValueC = new Ray(Vector3.zero, Vector3.up); - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Ray inValueA = default; - Ray inValueB = default; - Ray inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - [Test] - public void SerializeRay2D() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - var outValueA = new Ray2D(Vector2.zero, Vector2.up); - var outValueB = new Ray2D(Vector2.zero, Vector2.left); - var outValueC = new Ray2D(Vector2.zero, Vector2.right); - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - - // deserialize - Ray2D inValueA = default; - Ray2D inValueB = default; - Ray2D inValueC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - } - - private enum EnumA // int - { - A, - B, - C - } - - private enum EnumB : byte - { - X, - Y, - Z - } - - private enum EnumC : ushort - { - U, - N, - I, - T, - Y - } - - private enum EnumD : ulong - { - N, - E, - T - } - - [Test] - public void SerializeEnum() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - EnumA outValueA = EnumA.C; - EnumB outValueB = EnumB.X; - EnumC outValueC = EnumC.N; - EnumD outValueD = EnumD.T; - var outValueX = (EnumD)123; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outValueA); - outSerializer.Serialize(ref outValueB); - outSerializer.Serialize(ref outValueC); - outSerializer.Serialize(ref outValueD); - outSerializer.Serialize(ref outValueX); - - // deserialize - EnumA inValueA = default; - EnumB inValueB = default; - EnumC inValueC = default; - EnumD inValueD = default; - EnumD inValueX = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inValueA); - inSerializer.Serialize(ref inValueB); - inSerializer.Serialize(ref inValueC); - inSerializer.Serialize(ref inValueD); - inSerializer.Serialize(ref inValueX); - - // validate - Assert.AreEqual(inValueA, outValueA); - Assert.AreEqual(inValueB, outValueB); - Assert.AreEqual(inValueC, outValueC); - Assert.AreEqual(inValueD, outValueD); - Assert.AreEqual(inValueX, outValueX); - } - - [Test] - public void SerializeBoolArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - bool[] outArrayA = null; - bool[] outArrayB = new bool[0]; - bool[] outArrayC = { true, false, true }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - bool[] inArrayA = default; - bool[] inArrayB = default; - bool[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeCharArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - char[] outArrayA = null; - char[] outArrayB = new char[0]; - char[] outArrayC = { 'U', 'N', 'I', 'T', 'Y', '\0' }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - char[] inArrayA = default; - char[] inArrayB = default; - char[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeSbyteArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - sbyte[] outArrayA = null; - sbyte[] outArrayB = new sbyte[0]; - sbyte[] outArrayC = { -123, sbyte.MinValue, sbyte.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - sbyte[] inArrayA = default; - sbyte[] inArrayB = default; - sbyte[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeByteArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - byte[] outArrayA = null; - byte[] outArrayB = new byte[0]; - byte[] outArrayC = { 123, byte.MinValue, byte.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - byte[] inArrayA = default; - byte[] inArrayB = default; - byte[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeShortArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - short[] outArrayA = null; - short[] outArrayB = new short[0]; - short[] outArrayC = { 12345, short.MinValue, short.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - short[] inArrayA = default; - short[] inArrayB = default; - short[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeUshortArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - ushort[] outArrayA = null; - ushort[] outArrayB = new ushort[0]; - ushort[] outArrayC = { 12345, ushort.MinValue, ushort.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - ushort[] inArrayA = default; - ushort[] inArrayB = default; - ushort[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeIntArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - int[] outArrayA = null; - int[] outArrayB = new int[0]; - int[] outArrayC = { 1234567890, int.MinValue, int.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - int[] inArrayA = default; - int[] inArrayB = default; - int[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeUintArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - uint[] outArrayA = null; - uint[] outArrayB = new uint[0]; - uint[] outArrayC = { 1234567890, uint.MinValue, uint.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - uint[] inArrayA = default; - uint[] inArrayB = default; - uint[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeLongArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - long[] outArrayA = null; - long[] outArrayB = new long[0]; - long[] outArrayC = { 9876543210, long.MinValue, long.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - long[] inArrayA = default; - long[] inArrayB = default; - long[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeUlongArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - ulong[] outArrayA = null; - ulong[] outArrayB = new ulong[0]; - ulong[] outArrayC = { 9876543210, ulong.MinValue, ulong.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - ulong[] inArrayA = default; - ulong[] inArrayB = default; - ulong[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeFloatArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - float[] outArrayA = null; - float[] outArrayB = new float[0]; - float[] outArrayC = { 12345.6789f, float.MinValue, float.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - float[] inArrayA = default; - float[] inArrayB = default; - float[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeDoubleArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - double[] outArrayA = null; - double[] outArrayB = new double[0]; - double[] outArrayC = { 12345.6789, double.MinValue, double.MaxValue }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - double[] inArrayA = default; - double[] inArrayB = default; - double[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeStringArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - string[] outArrayA = null; - string[] outArrayB = new string[0]; - string[] outArrayC = { Guid.NewGuid().ToString("N"), string.Empty, null }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - string[] inArrayA = default; - string[] inArrayB = default; - string[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeColorArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Color[] outArrayA = null; - var outArrayB = new Color[0]; - Color[] outArrayC = { Color.black, Color.red, Color.white }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Color[] inArrayA = default; - Color[] inArrayB = default; - Color[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeColor32Array() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Color32[] outArrayA = null; - var outArrayB = new Color32[0]; - Color32[] outArrayC = - { - new Color32(0, 0, 0, byte.MaxValue), - new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue), - new Color32(byte.MaxValue, 0, 0, byte.MaxValue) - }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Color32[] inArrayA = default; - Color32[] inArrayB = default; - Color32[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeVector2Array() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Vector2[] outArrayA = null; - var outArrayB = new Vector2[0]; - Vector2[] outArrayC = { Vector2.up, Vector2.negativeInfinity, Vector2.positiveInfinity }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Vector2[] inArrayA = default; - Vector2[] inArrayB = default; - Vector2[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeVector3Array() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Vector3[] outArrayA = null; - var outArrayB = new Vector3[0]; - Vector3[] outArrayC = { Vector3.forward, Vector3.negativeInfinity, Vector3.positiveInfinity }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Vector3[] inArrayA = default; - Vector3[] inArrayB = default; - Vector3[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeVector4Array() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Vector4[] outArrayA = null; - var outArrayB = new Vector4[0]; - Vector4[] outArrayC = { Vector4.one, Vector4.negativeInfinity, Vector4.positiveInfinity }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Vector4[] inArrayA = default; - Vector4[] inArrayB = default; - Vector4[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeQuaternionArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Quaternion[] outArrayA = null; - var outArrayB = new Quaternion[0]; - Quaternion[] outArrayC = { Quaternion.identity, Quaternion.Euler(new Vector3(30, 45, -60)), Quaternion.Euler(new Vector3(90, -90, 180)) }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Quaternion[] inArrayA = default; - Quaternion[] inArrayB = default; - Quaternion[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.Null(inArrayA); - Assert.AreEqual(inArrayB, outArrayB); - for (int i = 0; i < outArrayC.Length; ++i) - { - Assert.Greater(Mathf.Abs(Quaternion.Dot(inArrayC[i], outArrayC[i])), 0.999f); - } - } - - [Test] - public void SerializeRayArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Ray[] outArrayA = null; - var outArrayB = new Ray[0]; - Ray[] outArrayC = - { - new Ray(Vector3.zero, Vector3.forward), - new Ray(Vector3.zero, Vector3.left), - new Ray(Vector3.zero, Vector3.up) - }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Ray[] inArrayA = default; - Ray[] inArrayB = default; - Ray[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeRay2DArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - Ray2D[] outArrayA = null; - var outArrayB = new Ray2D[0]; - Ray2D[] outArrayC = - { - new Ray2D(Vector2.zero, Vector2.up), - new Ray2D(Vector2.zero, Vector2.left), - new Ray2D(Vector2.zero, Vector2.right) - }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - Ray2D[] inArrayA = default; - Ray2D[] inArrayB = default; - Ray2D[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - - [Test] - public void SerializeEnumArray() - { - using var outStream = PooledNetworkBuffer.Get(); - using var outWriter = PooledNetworkWriter.Get(outStream); - using var inStream = PooledNetworkBuffer.Get(); - using var inReader = PooledNetworkReader.Get(inStream); - // serialize - EnumA[] outArrayA = null; - var outArrayB = new EnumB[0]; - EnumC[] outArrayC = { EnumC.U, EnumC.N, EnumC.I, EnumC.T, EnumC.Y, (EnumC)128 }; - var outSerializer = new NetworkSerializer(outWriter); - outSerializer.Serialize(ref outArrayA); - outSerializer.Serialize(ref outArrayB); - outSerializer.Serialize(ref outArrayC); - - // deserialize - EnumA[] inArrayA = default; - EnumB[] inArrayB = default; - EnumC[] inArrayC = default; - inStream.Write(outStream.ToArray()); - inStream.Position = 0; - var inSerializer = new NetworkSerializer(inReader); - inSerializer.Serialize(ref inArrayA); - inSerializer.Serialize(ref inArrayB); - inSerializer.Serialize(ref inArrayC); - - // validate - Assert.AreEqual(inArrayA, outArrayA); - Assert.AreEqual(inArrayB, outArrayB); - Assert.AreEqual(inArrayC, outArrayC); - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs.meta deleted file mode 100644 index 4f109557cd..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkSerializerTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 48b252004f20b4e28a4025ef8d0237fe -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Profiling.meta b/com.unity.netcode.gameobjects/Tests/Editor/Profiling.meta deleted file mode 100644 index 933cfaf938..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/Profiling.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 30b2fb9742b202343a827caae5ff5bb2 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs deleted file mode 100644 index f86baeffe7..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Unity.Netcode.EditorTests -{ - public class InternalMessageHandlerProfilingDecoratorTests - { - private InternalMessageHandlerProfilingDecorator m_Decorator; - - [SetUp] - public void Setup() - { - m_Decorator = new InternalMessageHandlerProfilingDecorator(new DummyMessageHandler(null)); - } - - [Test] - public void HandleSceneEventCallsUnderlyingHandler() - { - m_Decorator.HandleSceneEvent(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleSceneEvent)); - } - - [Test] - public void HandleUnnamedMessageCallsUnderlyingHandler() - { - m_Decorator.HandleUnnamedMessage(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleUnnamedMessage)); - } - - [Test] - public void HandleNamedMessageCallsUnderlyingHandler() - { - m_Decorator.HandleNamedMessage(0, null); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.HandleNamedMessage)); - } - - [Test] - public void MessageReceiveQueueItemCallsUnderlyingHandler() - { - m_Decorator.MessageReceiveQueueItem(0, null, 0.0f, MessageQueueContainer.MessageType.None); - - LogAssert.Expect(LogType.Log, nameof(m_Decorator.MessageReceiveQueueItem)); - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs.meta deleted file mode 100644 index 47c5f0cbc0..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/Profiling/InternalMessageHandlerProfilingDecoratorTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3212378d267799e40a84b65d2ed9591a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs deleted file mode 100644 index d592845c7a..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Unity.Netcode.RuntimeTests -{ - /// - /// Used in conjunction with the RpcQueueTest to validate that Rpcs are being invoked at the proper NetworkUpdateStage - /// - public class NetworkUpdateStagesComponent : NetworkBehaviour - { - /// - /// Allows the external RPCQueueTest to begin testing or stop it - /// - public bool EnableTesting; - - /// - /// How many times will we iterate through the various NetworkUpdateStage values? - /// (defaults to 1) - /// - public int MaxIterations = 1; - - private bool m_ClientReceivedRpc; - private int m_Counter; - private int m_MaxStagesSent; - private int m_MaxStages; - private ServerRpcParams m_ServerParams; - private ClientRpcParams m_ClientParams; - private NetworkUpdateStage m_LastUpdateStage; - - // Start is called before the first frame update - private void Start() - { - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; - m_ClientParams.Send.UpdateStage = NetworkUpdateStage.Update; - - //This assures the ILPP Codegen side of things is working by making sure that the Receive.UpdateStage - //is not always NetworkUpdateStage.EarlyUpdate as it should be whatever NetworkUpdateStage the RPC - //was assigned within the Send.UpdateStage - m_ServerParams.Receive.UpdateStage = NetworkUpdateStage.EarlyUpdate; - m_ClientParams.Receive.UpdateStage = NetworkUpdateStage.EarlyUpdate; - - m_MaxStages = (Enum.GetValues(typeof(NetworkUpdateStage)).Length); - m_MaxStagesSent = m_MaxStages * MaxIterations; - - //Start out with this being true (for first sequence) - m_ClientReceivedRpc = true; - } - - /// - /// Determine if we have iterated over more than our maximum stages we want to test - /// - /// true or false (did we exceed the max iterations or not?) - public bool ExceededMaxIterations() - { - if (m_StagesSent.Count > m_MaxStagesSent && m_MaxStagesSent > 0) - { - return true; - } - - return false; - } - - /// - /// Returns back whether the test has completed the total number of iterations - /// - /// - public bool IsTestComplete() - { - if (m_Counter >= MaxIterations) - { - return true; - } - - return false; - } - - // Update is called once per frame - private void Update() - { - if (NetworkManager.Singleton.IsListening && EnableTesting && m_ClientReceivedRpc) - { - //Reset this for the next sequence of rpcs - m_ClientReceivedRpc = false; - - //As long as testing isn't completed, keep testing - if (!IsTestComplete() && m_StagesSent.Count < m_MaxStagesSent) - { - m_LastUpdateStage = m_ServerParams.Send.UpdateStage; - m_StagesSent.Add(m_LastUpdateStage); - - PingMySelfServerRpc(m_StagesSent.Count, m_ServerParams); - - switch (m_ServerParams.Send.UpdateStage) - { - case NetworkUpdateStage.EarlyUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.FixedUpdate; - break; - case NetworkUpdateStage.FixedUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PreUpdate; - break; - case NetworkUpdateStage.PreUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.Update; - break; - case NetworkUpdateStage.Update: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PreLateUpdate; - break; - case NetworkUpdateStage.PreLateUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PostLateUpdate; - break; - case NetworkUpdateStage.PostLateUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; - break; - } - } - } - } - - private readonly List m_ServerStagesReceived = new List(); - private readonly List m_ClientStagesReceived = new List(); - private readonly List m_StagesSent = new List(); - - /// - /// Assures all update stages were in alginment with one another - /// - /// true or false - public bool ValidateUpdateStages() - { - var validated = false; - if (m_ServerStagesReceived.Count == m_ClientStagesReceived.Count && m_ClientStagesReceived.Count == m_StagesSent.Count) - { - for (int i = 0; i < m_StagesSent.Count; i++) - { - var currentStage = m_StagesSent[i]; - if (m_ServerStagesReceived[i] != currentStage) - { - Debug.Log($"ServerRpc Update Stage ({m_ServerStagesReceived[i]}) is not equal to the current update stage ({currentStage})"); - - return validated; - } - - if (m_ClientStagesReceived[i] != currentStage) - { - Debug.Log($"ClientRpc Update Stage ({m_ClientStagesReceived[i]}) is not equal to the current update stage ({currentStage})"); - - return validated; - } - } - - validated = true; - } - - return validated; - } - - /// - /// Server side RPC for testing - /// - /// server rpc parameters - [ServerRpc] - private void PingMySelfServerRpc(int currentCount, ServerRpcParams parameters = default) - { - Debug.Log($"{nameof(PingMySelfServerRpc)}: [HostClient][ServerRpc][{currentCount}] invoked during the {parameters.Receive.UpdateStage} stage."); - - m_ClientParams.Send.UpdateStage = parameters.Receive.UpdateStage; - m_ServerStagesReceived.Add(parameters.Receive.UpdateStage); - - PingMySelfClientRpc(currentCount, m_ClientParams); - } - - /// - /// Client Side RPC called by PingMySelfServerRPC to validate both Client->Server and Server-Client pipeline is working - /// - /// client rpc parameters - [ClientRpc] - private void PingMySelfClientRpc(int currentCount, ClientRpcParams parameters = default) - { - Debug.Log($"{nameof(PingMySelfClientRpc)}: [HostServer][ClientRpc][{currentCount}] invoked during the {parameters.Receive.UpdateStage} stage. (previous output line should confirm this)"); - - m_ClientStagesReceived.Add(parameters.Receive.UpdateStage); - - //If we reached the last update state, then go ahead and increment our iteration counter - if (parameters.Receive.UpdateStage == NetworkUpdateStage.PostLateUpdate) - { - m_Counter++; - } - - m_ClientReceivedRpc = true; - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs.meta deleted file mode 100644 index a765b6ca4b..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Components/NetworkUpdateStagesComponent.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2526efd7ad17baa4d953811c4c6520c7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs index c3e397b278..5b1191e39b 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs @@ -4,6 +4,7 @@ using System.IO; using System.Text; using NUnit.Framework; +using Unity.Collections; using UnityEngine; using UnityEngine.TestTools; @@ -21,29 +22,29 @@ public IEnumerator NamedMessageIsReceivedOnClientWithContent() { var messageName = Guid.NewGuid().ToString(); var messageContent = Guid.NewGuid().ToString(); - using var messageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageContent)); - - m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage( - messageName, - FirstClient.LocalClientId, - messageStream); + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(messageContent); + m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage( + messageName, + FirstClient.LocalClientId, + ref writer); + } ulong receivedMessageSender = 0; - string receivedMessageContent = null; + Guid receivedMessageContent; FirstClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, - (sender, stream) => + (ulong sender, ref FastBufferReader reader) => { receivedMessageSender = sender; - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - receivedMessageContent = Encoding.UTF8.GetString(memoryStream.ToArray()); + reader.ReadValueSafe(out receivedMessageContent); }); yield return new WaitForSeconds(0.2f); - Assert.NotNull(receivedMessageContent); Assert.AreEqual(messageContent, receivedMessageContent); Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender); } @@ -54,45 +55,44 @@ public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent() var messageName = Guid.NewGuid().ToString(); var messageContent = Guid.NewGuid().ToString(); using var messageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageContent)); - - m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage( - messageName, - new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, - messageStream); + + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(messageContent); + m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage( + messageName, + new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, + ref writer); + } ulong firstReceivedMessageSender = 0; - string firstReceivedMessageContent = null; + Guid firstReceivedMessageContent; FirstClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, - (sender, stream) => + (ulong sender, ref FastBufferReader reader) => { firstReceivedMessageSender = sender; - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - firstReceivedMessageContent = Encoding.UTF8.GetString(memoryStream.ToArray()); + reader.ReadValueSafe(out firstReceivedMessageContent); }); ulong secondReceivedMessageSender = 0; - string secondReceivedMessageContent = null; + Guid secondReceivedMessageContent; SecondClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, - (sender, stream) => + (ulong sender, ref FastBufferReader reader) => { secondReceivedMessageSender = sender; - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - secondReceivedMessageContent = Encoding.UTF8.GetString(memoryStream.ToArray()); + reader.ReadValueSafe(out secondReceivedMessageContent); }); yield return new WaitForSeconds(0.2f); - Assert.NotNull(firstReceivedMessageContent); Assert.AreEqual(messageContent, firstReceivedMessageContent); Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender); - Assert.NotNull(secondReceivedMessageContent); Assert.AreEqual(messageContent, secondReceivedMessageContent); Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs index f7d27b62fa..109fb01e46 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs @@ -4,6 +4,7 @@ using System.IO; using System.Text; using NUnit.Framework; +using Unity.Collections; using UnityEngine; using UnityEngine.TestTools; @@ -20,20 +21,24 @@ public class UnnamedMessageTests : BaseMultiInstanceTest public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() { var messageContent = Guid.NewGuid().ToString(); - var buffer = new NetworkBuffer(Encoding.UTF8.GetBytes(messageContent)); - - m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, buffer); + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(messageContent); + m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage( + FirstClient.LocalClientId, + ref writer); + } ulong receivedMessageSender = 0; string receivedMessageContent = null; - FirstClient.CustomMessagingManager.OnUnnamedMessage += (sender, stream) => - { - receivedMessageSender = sender; + FirstClient.CustomMessagingManager.OnUnnamedMessage += + (ulong sender, ref FastBufferReader reader) => + { + receivedMessageSender = sender; - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - receivedMessageContent = Encoding.UTF8.GetString(memoryStream.ToArray()); - }; + reader.ReadValueSafe(out receivedMessageContent); + }; yield return new WaitForSeconds(0.2f); @@ -46,31 +51,34 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() { var messageContent = Guid.NewGuid().ToString(); - var buffer = new NetworkBuffer(Encoding.UTF8.GetBytes(messageContent)); - - m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, buffer); + var writer = new FastBufferWriter(1300, Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(messageContent); + m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage( + new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, + ref writer); + } ulong firstReceivedMessageSender = 0; string firstReceivedMessageContent = null; - FirstClient.CustomMessagingManager.OnUnnamedMessage += (sender, stream) => - { - firstReceivedMessageSender = sender; + FirstClient.CustomMessagingManager.OnUnnamedMessage += + (ulong sender, ref FastBufferReader reader) => + { + firstReceivedMessageSender = sender; - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - firstReceivedMessageContent = Encoding.UTF8.GetString(memoryStream.ToArray()); - }; + reader.ReadValueSafe(out firstReceivedMessageContent); + }; ulong secondReceivedMessageSender = 0; string secondReceivedMessageContent = null; - SecondClient.CustomMessagingManager.OnUnnamedMessage += (sender, stream) => - { - secondReceivedMessageSender = sender; + SecondClient.CustomMessagingManager.OnUnnamedMessage += + (ulong sender, ref FastBufferReader reader) => + { + secondReceivedMessageSender = sender; - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - secondReceivedMessageContent = Encoding.UTF8.GetString(memoryStream.ToArray()); - }; + reader.ReadValueSafe(out firstReceivedMessageContent); + }; yield return new WaitForSeconds(0.2f); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index 6984baf2cc..b4067dcaa1 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -2,6 +2,7 @@ using UnityEngine; using UnityEngine.SceneManagement; using NUnit.Framework; +using Unity.Collections; namespace Unity.Netcode.RuntimeTests { @@ -18,148 +19,167 @@ public void NetworkObjectSceneSerializationFailure() { var networkObjectsToTest = new List(); - var pooledBuffer = PooledNetworkBuffer.Get(); - var writer = PooledNetworkWriter.Get(pooledBuffer); - var reader = PooledNetworkReader.Get(pooledBuffer); + var writer = new FastBufferWriter(1300, Allocator.Temp, 65536); var invalidNetworkObjectOffsets = new List(); var invalidNetworkObjectIdCount = new List(); var invalidNetworkObjects = new List(); var invalidNetworkObjectFrequency = 3; - - // Construct 50 NetworkObjects - for (int i = 0; i < 50; i++) + using (writer) { - // Inject an invalid NetworkObject every [invalidNetworkObjectFrequency] entry - if ((i % invalidNetworkObjectFrequency) == 0) - { - // Create the invalid NetworkObject - var gameObject = new GameObject($"InvalidTestObject{i}"); - Assert.IsNotNull(gameObject); - var networkObject = gameObject.AddComponent(); + // Construct 50 NetworkObjects + for (int i = 0; i < 50; i++) + { + // Inject an invalid NetworkObject every [invalidNetworkObjectFrequency] entry + if ((i % invalidNetworkObjectFrequency) == 0) + { + // Create the invalid NetworkObject + var gameObject = new GameObject($"InvalidTestObject{i}"); - Assert.IsNotNull(networkObject); + Assert.IsNotNull(gameObject); - var networkVariableComponent = gameObject.AddComponent(); - Assert.IsNotNull(networkVariableComponent); + var networkObject = gameObject.AddComponent(); - // Add invalid NetworkObject's starting position before serialization to handle trapping for the Debug.LogError message - // that we know will be thrown - invalidNetworkObjectOffsets.Add(pooledBuffer.Position); + Assert.IsNotNull(networkObject); - networkObject.GlobalObjectIdHash = (uint)(i); - invalidNetworkObjectIdCount.Add(i); + var networkVariableComponent = gameObject.AddComponent(); + Assert.IsNotNull(networkVariableComponent); - invalidNetworkObjects.Add(gameObject); + // Add invalid NetworkObject's starting position before serialization to handle trapping for the Debug.LogError message + // that we know will be thrown + invalidNetworkObjectOffsets.Add(writer.Position); - writer.WriteInt32Packed(networkObject.gameObject.scene.handle); - // Serialize the invalid NetworkObject - networkObject.SerializeSceneObject(writer, 0); + networkObject.GlobalObjectIdHash = (uint) (i); + invalidNetworkObjectIdCount.Add(i); - Debug.Log($"Invalid {nameof(NetworkObject)} Size {pooledBuffer.Position - invalidNetworkObjectOffsets[invalidNetworkObjectOffsets.Count - 1]}"); + invalidNetworkObjects.Add(gameObject); - // Now adjust how frequent we will inject invalid NetworkObjects - invalidNetworkObjectFrequency = Random.Range(2, 5); + writer.WriteValue((int) networkObject.gameObject.scene.handle); + // Serialize the invalid NetworkObject + var sceneObject = networkObject.GetMessageSceneObject(0); + sceneObject.Serialize(ref writer); - } - else - { - // Create a valid NetworkObject - var gameObject = new GameObject($"TestObject{i}"); + Debug.Log( + $"Invalid {nameof(NetworkObject)} Size {writer.Position - invalidNetworkObjectOffsets[invalidNetworkObjectOffsets.Count - 1]}"); - Assert.IsNotNull(gameObject); + // Now adjust how frequent we will inject invalid NetworkObjects + invalidNetworkObjectFrequency = Random.Range(2, 5); - var networkObject = gameObject.AddComponent(); + } + else + { + // Create a valid NetworkObject + var gameObject = new GameObject($"TestObject{i}"); - var networkVariableComponent = gameObject.AddComponent(); - Assert.IsNotNull(networkVariableComponent); + Assert.IsNotNull(gameObject); - Assert.IsNotNull(networkObject); + var networkObject = gameObject.AddComponent(); - networkObject.GlobalObjectIdHash = (uint)(i + 4096); + var networkVariableComponent = gameObject.AddComponent(); + Assert.IsNotNull(networkVariableComponent); - networkObjectsToTest.Add(gameObject); + Assert.IsNotNull(networkObject); - writer.WriteInt32Packed(networkObject.gameObject.scene.handle); + networkObject.GlobalObjectIdHash = (uint) (i + 4096); - // Handle populating the scenes loaded list - var scene = networkObject.gameObject.scene; + networkObjectsToTest.Add(gameObject); - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(scene.handle)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.Add(scene.handle, scene); - } + writer.WriteValue((int) networkObject.gameObject.scene.handle); - // Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(networkObject.gameObject.scene.handle)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle.Add(networkObject.gameObject.scene.handle, networkObject.gameObject.scene.handle); - } + // Handle populating the scenes loaded list + var scene = networkObject.gameObject.scene; - // Serialize the valid NetworkObject - networkObject.SerializeSceneObject(writer, 0); + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey( + scene.handle)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded + .Add(scene.handle, scene); + } - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey(networkObject.GlobalObjectIdHash)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(networkObject.GlobalObjectIdHash, new Dictionary()); - } - // Add this valid NetworkObject into the ScenePlacedObjects list - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects[networkObject.GlobalObjectIdHash].Add(SceneManager.GetActiveScene().handle, networkObject); - } - } + // Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle + .ContainsKey(networkObject.gameObject.scene.handle)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle + .Add(networkObject.gameObject.scene.handle, networkObject.gameObject.scene.handle); + } - var totalBufferSize = pooledBuffer.Position; - // Reset the position for reading this information - pooledBuffer.Position = 0; + // Serialize the valid NetworkObject + var sceneObject = networkObject.GetMessageSceneObject(0); + sceneObject.Serialize(ref writer); - var networkObjectsDeSerialized = new List(); - var currentLogLevel = NetworkManager.Singleton.LogLevel; - var invalidNetworkObjectCount = 0; - while (pooledBuffer.Position != totalBufferSize) - { - // If we reach the point where we expect it to fail, then make sure we let TestRunner know it should expect this log error message - if (invalidNetworkObjectOffsets.Count > 0 && pooledBuffer.Position == invalidNetworkObjectOffsets[0]) - { - invalidNetworkObjectOffsets.RemoveAt(0); - - // Turn off Network Logging to avoid other errors that we know will happen after the below LogAssert.Expect message occurs. - NetworkManager.Singleton.LogLevel = LogLevel.Nothing; + if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey( + networkObject.GlobalObjectIdHash)) + { + NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add( + networkObject.GlobalObjectIdHash, new Dictionary()); + } - // Trap for this specific error message so we don't make Test Runner think we failed (it will fail on Debug.LogError) - UnityEngine.TestTools.LogAssert.Expect(LogType.Error, $"Failed to spawn {nameof(NetworkObject)} for Hash {invalidNetworkObjectIdCount[invalidNetworkObjectCount]}."); - - invalidNetworkObjectCount++; + // Add this valid NetworkObject into the ScenePlacedObjects list + NetworkManagerHelper.NetworkManagerObject.SceneManager + .ScenePlacedObjects[networkObject.GlobalObjectIdHash] + .Add(SceneManager.GetActiveScene().handle, networkObject); + } } + var totalBufferSize = writer.Position; - NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(reader.ReadInt32Packed()); - - var deserializedNetworkObject = NetworkObject.DeserializeSceneObject(pooledBuffer, reader, NetworkManagerHelper.NetworkManagerObject); - if (deserializedNetworkObject != null) - { - networkObjectsDeSerialized.Add(deserializedNetworkObject); - } - else + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - // Under this condition, we are expecting null (i.e. no NetworkObject instantiated) - // and will set our log level back to the original value to assure the valid NetworkObjects - // aren't causing any log Errors to occur - NetworkManager.Singleton.LogLevel = currentLogLevel; - } - } - reader.Dispose(); - writer.Dispose(); - NetworkBufferPool.PutBackInPool(pooledBuffer); + var networkObjectsDeSerialized = new List(); + var currentLogLevel = NetworkManager.Singleton.LogLevel; + var invalidNetworkObjectCount = 0; + while (reader.Position != totalBufferSize) + { + // If we reach the point where we expect it to fail, then make sure we let TestRunner know it should expect this log error message + if (invalidNetworkObjectOffsets.Count > 0 && + reader.Position == invalidNetworkObjectOffsets[0]) + { + invalidNetworkObjectOffsets.RemoveAt(0); + + // Turn off Network Logging to avoid other errors that we know will happen after the below LogAssert.Expect message occurs. + NetworkManager.Singleton.LogLevel = LogLevel.Nothing; + + // Trap for this specific error message so we don't make Test Runner think we failed (it will fail on Debug.LogError) + UnityEngine.TestTools.LogAssert.Expect(LogType.Error, + $"Failed to spawn {nameof(NetworkObject)} for Hash {invalidNetworkObjectIdCount[invalidNetworkObjectCount]}."); + + invalidNetworkObjectCount++; + } + + + reader.ReadValue(out int handle); + NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(handle); + var sceneObject = new NetworkObject.SceneObject(); + sceneObject.Deserialize(ref reader); + + var deserializedNetworkObject = NetworkObject.AddSceneObject(sceneObject, ref reader, + NetworkManagerHelper.NetworkManagerObject); + if (deserializedNetworkObject != null) + { + networkObjectsDeSerialized.Add(deserializedNetworkObject); + } + else + { + // Under this condition, we are expecting null (i.e. no NetworkObject instantiated) + // and will set our log level back to the original value to assure the valid NetworkObjects + // aren't causing any log Errors to occur + NetworkManager.Singleton.LogLevel = currentLogLevel; + } + } - // Now validate all NetworkObjects returned against the original NetworkObjects we created - // after they validate, destroy the objects - foreach (var entry in networkObjectsToTest) - { - var entryNetworkObject = entry.GetComponent(); - Assert.IsTrue(networkObjectsDeSerialized.Contains(entryNetworkObject)); - Object.Destroy(entry); + // Now validate all NetworkObjects returned against the original NetworkObjects we created + // after they validate, destroy the objects + foreach (var entry in networkObjectsToTest) + { + var entryNetworkObject = entry.GetComponent(); + Assert.IsTrue(networkObjectsDeSerialized.Contains(entryNetworkObject)); + Object.Destroy(entry); + } + } } // Destroy the invalid network objects diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs index 9bf555c308..0796629872 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs @@ -25,25 +25,7 @@ public override bool IsDirty() { return Dirty; } - - public override void WriteDelta(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteBits((byte)1, 1); - writer.WriteInt32(k_DummyValue); - - DeltaWritten = true; - } - - public override void WriteField(Stream stream) - { - using var writer = PooledNetworkWriter.Get(stream); - writer.WriteBits((byte)1, 1); - writer.WriteInt32(k_DummyValue); - - FieldWritten = true; - } - + public override void WriteDelta(ref FastBufferWriter writer) { using (var bitWriter = writer.EnterBitwiseContext()) @@ -66,24 +48,6 @@ public override void WriteField(ref FastBufferWriter writer) FieldWritten = true; } - public override void ReadField(Stream stream) - { - using var reader = PooledNetworkReader.Get(stream); - reader.ReadBits(1); - Assert.AreEqual(k_DummyValue, reader.ReadInt32()); - - FieldRead = true; - } - - public override void ReadDelta(Stream stream, bool keepDirtyDelta) - { - using var reader = PooledNetworkReader.Get(stream); - reader.ReadBits(1); - Assert.AreEqual(k_DummyValue, reader.ReadInt32()); - - DeltaRead = true; - } - public override void ReadField(ref FastBufferReader reader) { using (var bitReader = reader.EnterBitwiseContext()) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs index c77bc80e07..62922f52fb 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs @@ -11,24 +11,10 @@ namespace Unity.Netcode.RuntimeTests public struct FixedString32Struct : INetworkSerializable { public FixedString32 FixedString; - public void NetworkSerialize(NetworkSerializer serializer) + + public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation { - if (serializer.IsReading) - { - var stringArraySize = 0; - serializer.Serialize(ref stringArraySize); - var stringArray = new char[stringArraySize]; - serializer.Serialize(ref stringArray); - var asString = new string(stringArray); - FixedString.CopyFrom(asString); - } - else - { - var stringArray = FixedString.Value.ToCharArray(); - var stringArraySize = stringArray.Length; - serializer.Serialize(ref stringArraySize); - serializer.Serialize(ref stringArray); - } + serializer.SerializeValue(ref FixedString); } } @@ -37,10 +23,10 @@ public struct TestStruct : INetworkSerializable public uint SomeInt; public bool SomeBool; - public void NetworkSerialize(NetworkSerializer serializer) + public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation { - serializer.Serialize(ref SomeInt); - serializer.Serialize(ref SomeBool); + serializer.SerializeValue(ref SomeInt); + serializer.SerializeValue(ref SomeBool); } } public class NetworkVariableTest : NetworkBehaviour diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/RpcPipelineTestComponent.cs b/com.unity.netcode.gameobjects/Tests/Runtime/RpcPipelineTestComponent.cs index aac8709ae0..07e7fee279 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/RpcPipelineTestComponent.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/RpcPipelineTestComponent.cs @@ -26,12 +26,6 @@ public class RpcPipelineTestComponent : NetworkBehaviour // Start is called before the first frame update private void Start() { - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; - m_ClientParams.Send.UpdateStage = NetworkUpdateStage.Update; - - m_ServerParams.Receive.UpdateStage = NetworkUpdateStage.EarlyUpdate; - m_ClientParams.Receive.UpdateStage = NetworkUpdateStage.EarlyUpdate; - m_MaxStagesSent = Enum.GetValues(typeof(NetworkUpdateStage)).Length * MaxIterations; //Start out with this being true (for first sequence) @@ -82,34 +76,9 @@ private void Update() m_ClientReceivedRpc = false; //As long as testing isn't completed, keep testing - if (!IsTestComplete() && m_StagesSent.Count < m_MaxStagesSent) + if (!IsTestComplete()) { - m_LastUpdateStage = m_ServerParams.Send.UpdateStage; - m_StagesSent.Add(m_LastUpdateStage); - PingMySelfServerRpc(m_StagesSent.Count, m_ServerParams); - - switch (m_ServerParams.Send.UpdateStage) - { - case NetworkUpdateStage.EarlyUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.FixedUpdate; - break; - case NetworkUpdateStage.FixedUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PreUpdate; - break; - case NetworkUpdateStage.PreUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.Update; - break; - case NetworkUpdateStage.Update: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PreLateUpdate; - break; - case NetworkUpdateStage.PreLateUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PostLateUpdate; - break; - case NetworkUpdateStage.PostLateUpdate: - m_ServerParams.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; - break; - } } } } @@ -159,10 +128,7 @@ public bool ValidateUpdateStages() [ServerRpc] private void PingMySelfServerRpc(int currentCount, ServerRpcParams parameters = default) { - Debug.Log($"{nameof(PingMySelfServerRpc)}: [HostClient][ServerRpc][{currentCount}] invoked during the {parameters.Receive.UpdateStage} stage."); - - m_ClientParams.Send.UpdateStage = parameters.Receive.UpdateStage; - m_ServerStagesReceived.Add(parameters.Receive.UpdateStage); + Debug.Log($"{nameof(PingMySelfServerRpc)}: [HostClient][ServerRpc][{currentCount}] invoked."); PingMySelfClientRpc(currentCount, m_ClientParams); } @@ -174,15 +140,7 @@ private void PingMySelfServerRpc(int currentCount, ServerRpcParams parameters = [ClientRpc] private void PingMySelfClientRpc(int currentCount, ClientRpcParams parameters = default) { - Debug.Log($"{nameof(PingMySelfClientRpc)}: [HostServer][ClientRpc][{currentCount}] invoked during the {parameters.Receive.UpdateStage} stage. (previous output line should confirm this)"); - - m_ClientStagesReceived.Add(parameters.Receive.UpdateStage); - - //If we reached the last update state, then go ahead and increment our iteration counter - if (parameters.Receive.UpdateStage == NetworkUpdateStage.PostLateUpdate) - { - m_Counter++; - } + Debug.Log($"{nameof(PingMySelfClientRpc)}: [HostServer][ClientRpc][{currentCount}] invoked. (previous output line should confirm this)"); m_ClientReceivedRpc = true; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs index f64da22995..64ef998118 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs @@ -21,49 +21,7 @@ public void Setup() // Create, instantiate, and host Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _)); } - - /// - /// Tests to make sure providing different - /// ** This does not include any of the Netcode to Transport code ** - /// - /// IEnumerator - [UnityTest, Order(1)] - public IEnumerator UpdateStagesInvocation() - { - Guid updateStagesTestId = NetworkManagerHelper.AddGameNetworkObject("UpdateStagesTest"); - var rpcPipelineTestComponent = NetworkManagerHelper.AddComponentToObject(updateStagesTestId); - - NetworkManagerHelper.SpawnNetworkObject(updateStagesTestId); - - var testsAreComplete = rpcPipelineTestComponent.IsTestComplete(); - var exceededMaximumStageIterations = rpcPipelineTestComponent.ExceededMaxIterations(); - - // Start testing - rpcPipelineTestComponent.EnableTesting = true; - - Debug.Log("Running TestNetworkUpdateStages: "); - - // Wait for the RPC pipeline test to complete or if we exceeded the maximum iterations bail - while (!testsAreComplete && !exceededMaximumStageIterations) - { - yield return new WaitForSeconds(0.003f); - - testsAreComplete = rpcPipelineTestComponent.IsTestComplete(); - Assert.IsFalse(rpcPipelineTestComponent.ExceededMaxIterations()); - } - var testsAreValidated = rpcPipelineTestComponent.ValidateUpdateStages(); - - // Stop testing - rpcPipelineTestComponent.EnableTesting = false; - - Debug.Log($"Exiting status => {nameof(testsAreComplete)}: {testsAreComplete} - {nameof(testsAreValidated)}: {testsAreValidated} -{nameof(exceededMaximumStageIterations)}: {exceededMaximumStageIterations}"); - - Assert.IsTrue(testsAreComplete && testsAreValidated); - - // Disable this so it isn't running any longer. - rpcPipelineTestComponent.gameObject.SetActive(false); - } - + /// /// This tests the RPC Queue outbound and inbound buffer capabilities. /// @@ -106,60 +64,6 @@ public IEnumerator BufferDataValidation() [Test, Order(3)] public void RpcQueueContainerClass() { - // Create a testing rpcQueueContainer that doesn't get added to the network update loop so we don't try to send or process during the test - var rpcQueueContainer = new MessageQueueContainer(NetworkManagerHelper.NetworkManagerObject, 0, true); - - // Make sure we set testing mode so we don't try to invoke RPCs - rpcQueueContainer.SetTestingState(true); - - const int maxRpcEntries = 8; - const int messageChunkSize = 2048; - - var preCalculatedBufferValues = new List(messageChunkSize); - for (int i = 0; i < messageChunkSize; i++) - { - preCalculatedBufferValues.AddRange(BitConverter.GetBytes(UnityEngine.Random.Range(0, ulong.MaxValue))); - } - - ulong senderNetworkId = 1; - - var randomGeneratedDataArray = preCalculatedBufferValues.ToArray(); - // Create fictitious list of clients to send to - var pseudoClients = new ulong[] { 0 }; - // Testing outbound side of the RpcQueueContainer - for (int i = 0; i < maxRpcEntries; i++) - { - // Increment our offset into our randomly generated data for next entry; - - var writer = rpcQueueContainer.BeginAddQueueItemToFrame(MessageQueueContainer.MessageType.ServerRpc, Time.realtimeSinceStartup, NetworkDelivery.Reliable, - senderNetworkId, pseudoClients, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - writer.WriteByteArray(randomGeneratedDataArray, messageChunkSize); - - rpcQueueContainer.EndAddQueueItemToFrame(writer, MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - } - - // Now verify the data by obtaining the RpcQueueHistoryFrame we just wrote to - var currentFrame = rpcQueueContainer.GetLoopBackHistoryFrame(MessageQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate); - - // Reset our index offset - // Parse through the entries written to the current RpcQueueHistoryFrame - var currentQueueItem = currentFrame.GetFirstQueueItem(); - while (currentQueueItem.MessageType != MessageQueueContainer.MessageType.None) - { - // Check to make sure the wrapper information is accurate for the entry - Assert.AreEqual(currentQueueItem.NetworkId, senderNetworkId); - Assert.AreEqual(currentQueueItem.MessageType, MessageQueueContainer.MessageType.ServerRpc); - Assert.AreEqual(currentQueueItem.UpdateStage, NetworkUpdateStage.PostLateUpdate); - Assert.AreEqual(currentQueueItem.Delivery, NetworkDelivery.Reliable); - - // Validate the data in the queue - Assert.IsTrue(NetworkManagerHelper.BuffersMatch(currentQueueItem.MessageData.Offset, messageChunkSize, currentQueueItem.MessageData.Array, randomGeneratedDataArray)); - - // Prepare for next queue item - currentQueueItem = currentFrame.GetNextQueueItem(); - } - - rpcQueueContainer.Dispose(); } diff --git a/testproject/Assets/Resources/PerformanceTestRunInfo.json b/testproject/Assets/Resources/PerformanceTestRunInfo.json deleted file mode 100644 index 755720af66..0000000000 --- a/testproject/Assets/Resources/PerformanceTestRunInfo.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "TestSuite": null, - "Date": 1631066863434, - "Player": { - "Development": false, - "ScreenWidth": 0, - "ScreenHeight": 0, - "ScreenRefreshRate": 0, - "Fullscreen": false, - "Vsync": 0, - "AntiAliasing": 0, - "Batchmode": false, - "RenderThreadingMode": "MultiThreaded", - "GpuSkinning": true, - "Platform": null, - "ColorSpace": null, - "AnisotropicFiltering": null, - "BlendWeights": null, - "GraphicsApi": null, - "ScriptingBackend": "IL2CPP", - "AndroidTargetSdkVersion": "AndroidApiLevelAuto", - "AndroidBuildSystem": "Gradle", - "BuildTarget": "WebGL", - "StereoRenderingPath": "MultiPass" - }, - "Hardware": null, - "Editor": { - "Version": "2020.3.12f1", - "Branch": "2020.3/release", - "Changeset": "b3b2c6512326", - "Date": 1623142004 - }, - "Dependencies": [ - "com.unity.collab-proxy@1.5.7", - "com.unity.ide.rider@3.0.5", - "com.unity.ide.visualstudio@2.0.8", - "com.unity.ide.vscode@1.2.3", - "com.unity.netcode.gameobjects@0.2.0", - "com.unity.multiplayer.transport.utp@0.0.1-preview.1", - "com.unity.package-validation-suite@0.19.2-preview", - "com.unity.test-framework@1.1.27", - "com.unity.test-framework.performance@2.3.1-preview", - "com.unity.textmeshpro@3.0.6", - "com.unity.timeline@1.5.2", - "com.unity.ugui@1.0.0", - "com.unity.modules.ai@1.0.0", - "com.unity.modules.androidjni@1.0.0", - "com.unity.modules.animation@1.0.0", - "com.unity.modules.assetbundle@1.0.0", - "com.unity.modules.audio@1.0.0", - "com.unity.modules.cloth@1.0.0", - "com.unity.modules.director@1.0.0", - "com.unity.modules.imageconversion@1.0.0", - "com.unity.modules.imgui@1.0.0", - "com.unity.modules.jsonserialize@1.0.0", - "com.unity.modules.particlesystem@1.0.0", - "com.unity.modules.physics@1.0.0", - "com.unity.modules.physics2d@1.0.0", - "com.unity.modules.screencapture@1.0.0", - "com.unity.modules.terrain@1.0.0", - "com.unity.modules.terrainphysics@1.0.0", - "com.unity.modules.tilemap@1.0.0", - "com.unity.modules.ui@1.0.0", - "com.unity.modules.uielements@1.0.0", - "com.unity.modules.umbra@1.0.0", - "com.unity.modules.unityanalytics@1.0.0", - "com.unity.modules.unitywebrequest@1.0.0", - "com.unity.modules.unitywebrequestassetbundle@1.0.0", - "com.unity.modules.unitywebrequestaudio@1.0.0", - "com.unity.modules.unitywebrequesttexture@1.0.0", - "com.unity.modules.unitywebrequestwww@1.0.0", - "com.unity.modules.vehicles@1.0.0", - "com.unity.modules.video@1.0.0", - "com.unity.modules.vr@1.0.0", - "com.unity.modules.wind@1.0.0", - "com.unity.modules.xr@1.0.0" - ], - "Results": [] -} \ No newline at end of file diff --git a/testproject/Assets/Resources/PerformanceTestRunInfo.json.meta b/testproject/Assets/Resources/PerformanceTestRunInfo.json.meta deleted file mode 100644 index b3b02decde..0000000000 --- a/testproject/Assets/Resources/PerformanceTestRunInfo.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 478110403df6de644a875e5199b9d022 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs index f2d0f32679..6fb29632c9 100644 --- a/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs +++ b/testproject/Assets/Samples/PrefabPool/NetworkPrefabHandlerObjectPoolOverride.cs @@ -124,12 +124,6 @@ private GameObject GetNextSpawnObject(int synchronizedIndex = -1) return null; } - public void OnSynchronizeWrite(NetworkWriter networkWriter, NetworkObject networkObject) - { - var genericObjectPooledBehaviour = NetworkObject.GetComponent(); - networkWriter.WriteInt32Packed(genericObjectPooledBehaviour.SyncrhonizedObjectTypeIndex); - } - public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { var gameObject = GetNextSpawnObject(); diff --git a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs index e599b080bb..9a54aa25dc 100644 --- a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs +++ b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs @@ -281,7 +281,6 @@ private void InitializeNetworkManager() NetworkManager.StartClient(); Screen.SetResolution(800, 80, FullScreenMode.Windowed); } - m_ServerRpcParams.Send.UpdateStage = NetworkUpdateStage.Update; break; } @@ -292,7 +291,6 @@ private void InitializeNetworkManager() NetworkManager.StartHost(); Screen.SetResolution(800, 480, FullScreenMode.Windowed); } - m_ClientRpcParams.Send.UpdateStage = NetworkUpdateStage.PreUpdate; break; } @@ -305,8 +303,6 @@ private void InitializeNetworkManager() m_ClientProgressBar.enabled = false; } - m_ClientRpcParams.Send.UpdateStage = NetworkUpdateStage.PostLateUpdate; - break; } } @@ -645,7 +641,6 @@ private void OnSendCounterServerRpc(int counter, ulong clientId, ServerRpcParams private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) { m_ClientRpcParamsMultiParameter.Send.TargetClientIds[0] = parameters.Receive.SenderClientId; - m_ClientRpcParamsMultiParameter.Send.UpdateStage = NetworkUpdateStage.Update; OnSendNoParametersClientRpc(m_ClientRpcParamsMultiParameter); } @@ -658,7 +653,6 @@ private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) private void OnSendMultiParametersServerRpc(int count, float floatValue, long longValue, ServerRpcParams parameters = default) { m_ClientRpcParamsMultiParameter.Send.TargetClientIds[0] = parameters.Receive.SenderClientId; - m_ClientRpcParamsMultiParameter.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate; OnSendMultiParametersClientRpc(count, floatValue, longValue, m_ClientRpcParamsMultiParameter); } diff --git a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs index 610e63e264..31e218be28 100644 --- a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs +++ b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs @@ -1,8 +1,30 @@ +using Unity.Collections; using UnityEngine; using Unity.Netcode; namespace TestProject.ManualTests { + class Foo + { + public int I; + } + + static class FastBufferWriterExtensions + { + public static void WriteValueSafe(this ref FastBufferWriter writer, in Foo value) + { + writer.WriteValueSafe(value.I); + } + + + public static void ReadValueSafe(this ref FastBufferReader reader, out Foo value) + { + value = new Foo(); + reader.ReadValueSafe(out value.I); + } + + } + /// /// Used with GenericObjects to randomly move them around /// @@ -54,6 +76,7 @@ public void Move(int speed) } } + // We just apply our current direction with magnitude to our current position during fixed update private void FixedUpdate() { @@ -79,6 +102,67 @@ private void ChangeDirectionClientRpc(Vector3 direction) { m_Direction = direction; } + private void ChangeDirectionClientRpcManual(Vector3 direction) + { + //IL_00d1: Unknown result type (might be due to invalid IL or missing references) + //IL_00d2: Unknown result type (might be due to invalid IL or missing references) + NetworkManager networkManager = base.NetworkManager; + if (networkManager == null || !networkManager.IsListening) + { + return; + } + if (__rpc_exec_stage != __RpcExecStage.Client && (networkManager.IsServer || networkManager.IsHost)) + { + FastBufferWriter writer = new FastBufferWriter(1300, (Allocator)2, 1300); + try + { + writer.WriteValueSafe(in direction); + ClientRpcParams sendParams = default(ClientRpcParams); + SendClientRpc(ref writer, 4099941514u, sendParams, RpcDelivery.Reliable); + } + finally + { + writer.Dispose(); + } + } + if (__rpc_exec_stage == __RpcExecStage.Client && (networkManager.IsClient || networkManager.IsHost)) + { + m_Direction = direction; + } + } + + [ClientRpc] + private void ChangeDirectionClientRpc(Foo testFoo) + { + } + private void ChangeDirectionClientRpcManual(NetworkBehaviour testFoo) + { + //IL_00d1: Unknown result type (might be due to invalid IL or missing references) + //IL_00d2: Unknown result type (might be due to invalid IL or missing references) + NetworkManager networkManager = base.NetworkManager; + if (networkManager == null || !networkManager.IsListening) + { + return; + } + if (__rpc_exec_stage != __RpcExecStage.Client && (networkManager.IsServer || networkManager.IsHost)) + { + FastBufferWriter writer = new FastBufferWriter(1300, (Allocator)2, 1300); + try + { + writer.WriteValueSafe(testFoo); + ClientRpcParams sendParams = default(ClientRpcParams); + SendClientRpc(ref writer, 4099941514u, sendParams, RpcDelivery.Reliable); + } + finally + { + writer.Dispose(); + } + } + if (__rpc_exec_stage == __RpcExecStage.Client && (networkManager.IsClient || networkManager.IsHost)) + { + } + } + private void OnCollisionStay(Collision collision) { diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs b/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs index 0818c85834..9ba8902016 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs @@ -11,18 +11,18 @@ public struct StatsInfoContainer : INetworkSerializable { public List StatValues; - public void NetworkSerialize(NetworkSerializer serializer) + public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation { - if (serializer.IsReading) + if (serializer.IsReader) { float[] statValuesArray = null; - serializer.Serialize(ref statValuesArray); + serializer.SerializeValue(ref statValuesArray); StatValues = new List(statValuesArray); } else { float[] statValuesArray = StatValues?.ToArray() ?? new float[0]; - serializer.Serialize(ref statValuesArray); + serializer.SerializeValue(ref statValuesArray); } } } diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 99beb6ec62..6c9cb70d1f 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using TestProject.RuntimeTests.Support; using UnityEngine; +using UnityEngine.PlayerLoop; using UnityEngine.TestTools; namespace TestProject.RuntimeTests @@ -97,19 +98,14 @@ public IEnumerator SpawnChangeOwnership() } [UnityTest] - public IEnumerator SpawnRpcDespawn([Values] NetworkUpdateStage testStage) + public IEnumerator SpawnRpcDespawn() { - // Neither of these is supported for sending RPCs. - if (testStage == NetworkUpdateStage.Unset || testStage == NetworkUpdateStage.Initialization || testStage == NetworkUpdateStage.FixedUpdate) - { - yield break; - } // Must be 1 for this test. const int numClients = 1; Assert.True(MultiInstanceHelpers.Create(numClients, out NetworkManager server, out NetworkManager[] clients)); m_Prefab = new GameObject("Object"); m_Prefab.AddComponent(); - Support.SpawnRpcDespawn.TestStage = testStage; + Support.SpawnRpcDespawn.TestStage = NetworkUpdateStage.EarlyUpdate; var networkObject = m_Prefab.AddComponent(); // Make it a prefab @@ -168,7 +164,7 @@ public IEnumerator SpawnRpcDespawn([Values] NetworkUpdateStage testStage) yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); } - Assert.AreEqual(testStage, Support.SpawnRpcDespawn.StageExecutedByReceiver); + Assert.AreEqual(NetworkUpdateStage.EarlyUpdate, Support.SpawnRpcDespawn.StageExecutedByReceiver); Assert.AreEqual(Support.SpawnRpcDespawn.ServerUpdateCount, Support.SpawnRpcDespawn.ClientUpdateCount); Assert.True(handler.WasSpawned); var lastFrameNumber = Time.frameCount + 1; diff --git a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs b/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs index bda3c1c7cb..f95b685d11 100644 --- a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs +++ b/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; @@ -381,19 +382,22 @@ public class UserSerializableClass : INetworkSerializable public ulong MyulongValue; public List MyByteListValues; - public void NetworkSerialize(NetworkSerializer serializer) + public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation { - if (serializer.IsReading) + serializer.SerializeValue(ref MyintValue); + serializer.SerializeValue(ref MyulongValue); + int size = MyByteListValues.Count; + serializer.SerializeValue(ref size); + if (serializer.IsReader) { - MyintValue = serializer.Reader.ReadInt32Packed(); - MyulongValue = serializer.Reader.ReadUInt64Packed(); - MyByteListValues = new List(serializer.Reader.ReadByteArray()); + var b = new byte[size]; + serializer.GetFastBufferReader().ReadBytesSafe(ref b, size); + MyByteListValues = new List(b); } else { - serializer.Writer.WriteInt32Packed(MyintValue); - serializer.Writer.WriteUInt64Packed(MyulongValue); - serializer.Writer.WriteByteArray(MyByteListValues.ToArray()); + + serializer.GetFastBufferWriter().WriteBytesSafe(MyByteListValues.ToArray()); } } diff --git a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs index 31f71ec1b4..e5389ce4f1 100644 --- a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs +++ b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs @@ -24,25 +24,12 @@ public override IEnumerator Setup() yield break; } - /// - /// Default Mode (Batched RPCs Enabled) - /// - /// [UnityTest] public IEnumerator ManualRpcTestsAutomated() { return AutomatedRpcTestsHandler(9); } - /// - /// Same test with Batched RPC turned off - /// - /// - [UnityTest] - public IEnumerator ManualRpcTestsAutomatedNoBatching() - { - return AutomatedRpcTestsHandler(3, false); - } /// /// This just helps to simplify any further tests that can leverage from @@ -52,9 +39,8 @@ public IEnumerator ManualRpcTestsAutomatedNoBatching() /// RPC Batching is enabled or not. /// /// - /// /// - private IEnumerator AutomatedRpcTestsHandler(int numClients, bool useBatching = true) + private IEnumerator AutomatedRpcTestsHandler(int numClients) { var startFrameCount = Time.frameCount; var startTime = Time.realtimeSinceStartup; @@ -68,14 +54,6 @@ private IEnumerator AutomatedRpcTestsHandler(int numClients, bool useBatching = playerPrefab.AddComponent(); }); - // Set the RPC Batch sending mode - m_ServerNetworkManager.MessageQueueContainer.EnableBatchedMessages(useBatching); - - for (int i = 0; i < m_ClientNetworkManagers.Length; i++) - { - m_ClientNetworkManagers[i].MessageQueueContainer.EnableBatchedMessages(useBatching); - } - // [Host-Side] Get the Host owned instance of the RpcQueueManualTests var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult)); diff --git a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawn.cs b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawn.cs index 0b7ac56369..948a89ef83 100644 --- a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawn.cs +++ b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawn.cs @@ -17,7 +17,7 @@ public class SpawnRpcDespawn : NetworkBehaviour, INetworkUpdateSystem [ClientRpc] public void SendIncrementUpdateCountClientRpc() { - Assert.AreEqual(TestStage, NetworkUpdateLoop.UpdateStage); + Assert.AreEqual(NetworkUpdateStage.EarlyUpdate, NetworkUpdateLoop.UpdateStage); StageExecutedByReceiver = NetworkUpdateLoop.UpdateStage; ++ClientUpdateCount; @@ -40,7 +40,7 @@ public void Activate() public void NetworkStart() { Debug.Log($"Network Start on client {NetworkManager.LocalClientId.ToString()}"); - Assert.AreEqual(TestStage, NetworkUpdateLoop.UpdateStage); + Assert.AreEqual(NetworkUpdateStage.EarlyUpdate, NetworkUpdateLoop.UpdateStage); } public void Awake() diff --git a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs index 5e2c5b2990..c1f8496062 100644 --- a/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs +++ b/testproject/Assets/Tests/Runtime/Support/SpawnRpcDespawnInstanceHandler.cs @@ -19,7 +19,7 @@ public SpawnRpcDespawnInstanceHandler(uint prefabHash) public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { WasSpawned = true; - Assert.AreEqual(SpawnRpcDespawn.TestStage, NetworkUpdateLoop.UpdateStage); + Assert.AreEqual(NetworkUpdateStage.EarlyUpdate, NetworkUpdateLoop.UpdateStage); // See if there is a valid registered NetworkPrefabOverrideLink associated with the provided prefabHash @@ -60,7 +60,7 @@ public void Destroy(NetworkObject networkObject) WasDestroyed = true; if (networkObject.NetworkManager.IsClient) { - Assert.AreEqual(NetworkUpdateStage.PostLateUpdate, NetworkUpdateLoop.UpdateStage); + Assert.AreEqual(NetworkUpdateStage.EarlyUpdate, NetworkUpdateLoop.UpdateStage); } Object.Destroy(networkObject.gameObject); From 90f41be7c590766022a1ec2a89c8d99b39adb1f9 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 13 Sep 2021 21:44:28 -0500 Subject: [PATCH 29/58] Removed IMessageHandler as it's no longer needed after DelayUtil went away. --- .../Runtime/Messaging/IMessageHandler.cs | 8 -------- .../Runtime/Messaging/IMessageHandler.cs.meta | 11 ----------- .../Runtime/Messaging/MessagingSystem.cs | 2 +- 3 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs deleted file mode 100644 index 65beef8978..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Unity.Netcode -{ - public interface IMessageHandler - { - public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, - float timestamp); - } -} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta deleted file mode 100644 index c4db58e397..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageHandler.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 24cbf54292ac5c143b08c95aa07e18c5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index ed05a90705..fe3f506cc7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -17,7 +17,7 @@ public InvalidMessageStructureException() { } public InvalidMessageStructureException(string issue) : base(issue) { } } - public class MessagingSystem: IMessageHandler, IDisposable + public class MessagingSystem: IDisposable { #region Internal Types private struct ReceiveQueueItem From 295709512ae48f83650f4596fdea961d60fa7cb4 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 16:18:47 -0500 Subject: [PATCH 30/58] Fixed tests. --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 13 +- .../Runtime/Core/NetworkBehaviour.cs | 98 +++++++------- .../Runtime/Core/NetworkManager.cs | 34 +++-- .../Runtime/Core/NetworkObject.cs | 22 ++++ .../Runtime/Core/SnapshotSystem.cs | 7 +- .../Runtime/Messaging/CustomMessageManager.cs | 11 +- .../Messages/ChangeOwnershipMessage.cs | 4 +- .../Messages/ConnectionRequestMessage.cs | 2 - .../Messages/NetworkVariableDeltaMessage.cs | 10 +- .../Messaging/Messages/ParentSyncMessage.cs | 8 +- .../Runtime/Messaging/MessagingSystem.cs | 20 ++- .../Collections/NetworkDictionary.cs | 24 ++-- .../Collections/NetworkList.cs | 44 +++---- .../NetworkVariable/Collections/NetworkSet.cs | 24 ++-- .../NetworkVariable/NetworkVariable.cs | 4 +- .../Runtime/SceneManagement/SceneEventData.cs | 8 +- .../Runtime/Serialization/FastBufferReader.cs | 4 +- .../Runtime/Utility/DynamicUnmanagedArray.cs | 2 +- .../BufferDataValidationComponent.cs | 2 +- .../Runtime/Messaging/NamedMessageTests.cs | 6 +- .../Runtime/Messaging/UnnamedMessageTests.cs | 15 +-- .../NetworkObjectSceneSerializationTests.cs | 13 +- .../Tests/Runtime/NetworkVarBufferCopyTest.cs | 4 + .../Tests/Runtime/RpcQueueTests.cs | 10 -- .../ObjectToNotDestroyBehaviour.cs | 2 +- .../Tests/Manual/Scripts/RandomMovement.cs | 83 +----------- .../Tests/Runtime/DontDestroyOnLoadTests.cs | 4 +- ...dUpdateMessagesAreOnlyProcessedOnceTest.cs | 120 ------------------ ...teMessagesAreOnlyProcessedOnceTest.cs.meta | 3 - .../Runtime/MultiClientConnectionApproval.cs | 4 +- 30 files changed, 223 insertions(+), 382 deletions(-) delete mode 100644 testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs delete mode 100644 testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs.meta diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 0c59d3a367..9a8f9510ee 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -912,11 +912,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA } } - // var writer = new FastBufferWriter(1300, Allocator.Temp, 1300); + // var writer = new FastBufferWriter(1300, Allocator.Temp, 65536); instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300)); instructions.Add(processor.Create(OpCodes.Ldc_I4_2)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, 65536)); instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Constructor)); var firstInstruction = processor.Create(OpCodes.Nop); @@ -1081,8 +1081,9 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA } { + // TODO: Figure out why try/catch here cause the try block not to execute at all. // End try block - instructions.Add(processor.Create(OpCodes.Leave, lastInstr)); + //instructions.Add(processor.Create(OpCodes.Leave, lastInstr)); // writer.Dispose(); var handlerFirst = processor.Create(OpCodes.Ldloca, serializerLocIdx); @@ -1090,17 +1091,17 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Dispose)); // End finally block - instructions.Add(processor.Create(OpCodes.Endfinally)); + //instructions.Add(processor.Create(OpCodes.Endfinally)); // try { ... serialization code ... } finally { writer.Dispose(); } - var handler = new ExceptionHandler(ExceptionHandlerType.Finally) + /*var handler = new ExceptionHandler(ExceptionHandlerType.Finally) { TryStart = firstInstruction, TryEnd = handlerFirst, HandlerStart = handlerFirst, HandlerEnd = lastInstr }; - processor.Body.ExceptionHandlers.Add(handler); + processor.Body.ExceptionHandlers.Add(handler);*/ } instructions.Add(lastInstr); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index b4b3bf9bdb..42b6714b1b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -45,9 +45,13 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR switch (delivery) { case RpcDelivery.Reliable: - networkDelivery = NetworkDelivery.ReliableSequenced; + networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: + if (writer.Length > 1300) + { + throw new OverflowException("RPC parameters are too large for unreliable delivery."); + } networkDelivery = NetworkDelivery.Unreliable; break; } @@ -63,7 +67,7 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR }, RPCData = writer }; - var rpcMessageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ServerClientId); + var rpcMessageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ServerClientId, true); #if DEVELOPMENT_BUILD || UNITY_EDITOR if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName)) { @@ -83,9 +87,13 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, switch (delivery) { case RpcDelivery.Reliable: - networkDelivery = NetworkDelivery.ReliableSequenced; + networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: + if (writer.Length > 1300) + { + throw new OverflowException("RPC parameters are too large for unreliable delivery."); + } networkDelivery = NetworkDelivery.Unreliable; break; } @@ -105,7 +113,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, if (sendParams.Send.TargetClientIds != null) { - messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIds); + messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIds, true); } else if (sendParams.Send.TargetClientIdsNativeArray != null) { @@ -117,7 +125,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, } else { - messageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ConnectedClientsIds); + messageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ConnectedClientsIds, true); } #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -429,7 +437,25 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) ClientId = clientId, DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j] }; - NetworkManager.SendMessage(message, m_DeliveryTypesForNetworkVariableGroups[j], clientId); + // TODO: Serialization is where the IsDirty flag gets changed. + // Messages don't get sent from the server to itself, so if we're host and sending to ourselves, + // we still have to actually serialize the message even though we're not sending it, otherwise + // the dirty flag doesn't change properly. These two pieces should be decoupled at some point + // so we don't have to do this serialization work if we're not going to use the result. + if (IsServer && clientId == NetworkManager.ServerClientId) + { + var tmpWriter = new FastBufferWriter(1300, Allocator.Temp); +#pragma warning disable CS0728 // Warns that tmpWriter may be reassigned within Serialize, but Serialize does not reassign it. + using (tmpWriter) + { + message.Serialize(ref tmpWriter); + } +#pragma warning restore CS0728 // Warns that tmpWriter may be reassigned within Serialize, but Serialize does not reassign it. + } + else + { + NetworkManager.SendMessage(message, m_DeliveryTypesForNetworkVariableGroups[j], clientId); + } } } } @@ -460,33 +486,22 @@ internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong client { bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId); - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + if (canClientRead) { - if (!canClientRead) - { - writer.WriteValue((ushort)0); - } + var writePos = writer.Position; + writer.WriteValueSafe((ushort)0); + var startPos = writer.Position; + NetworkVariableFields[j].WriteField(ref writer); + var size = writer.Position - startPos; + writer.Seek(writePos); + writer.WriteValueSafe((ushort)size); + writer.Seek(startPos + size); } else { - writer.WriteValue(canClientRead); - } - - if (canClientRead) - { - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - var tmpWriter = new FastBufferWriter(1300, Allocator.Temp, Int16.MaxValue); - NetworkVariableFields[j].WriteField(ref tmpWriter); - - writer.WriteValue((ushort)tmpWriter.Length); - tmpWriter.CopyTo(ref writer); - } - else - { - NetworkVariableFields[j].WriteField(ref writer); - } + writer.WriteValueSafe((ushort)0); } + writer.WriteValueSafe((ushort)0x12AB); } } @@ -499,24 +514,16 @@ internal void SetNetworkVariableData(ref FastBufferReader reader) for (int j = 0; j < NetworkVariableFields.Count; j++) { - ushort varSize = 0; - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + reader.ReadValueSafe(out ushort varSize); + ushort magic; + if (varSize == 0) { - reader.ReadValue(out varSize); - - if (varSize == 0) + reader.ReadValueSafe(out magic); + if (magic != (ushort) 0x12AB) { - continue; - } - } - else - { - reader.ReadValue(out bool clientCanRead); - if (!clientCanRead) - { - continue; + NetworkLog.LogWarning($"Var data ended not on the magic value."); } + continue; } var readStartPos = reader.Position; @@ -543,6 +550,11 @@ internal void SetNetworkVariableData(ref FastBufferReader reader) reader.Seek(readStartPos + varSize); } } + reader.ReadValueSafe(out magic); + if (magic != (ushort) 0x12AB) + { + NetworkLog.LogWarning($"Var data ended not on the magic value."); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 38f383b07a..b78f87a67a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -839,6 +839,8 @@ public SocketTasks StartHost() Initialize(true); var socketTasks = NetworkConfig.NetworkTransport.StartServer(); + m_MessagingSystem.ClientConnected(ServerClientId); + LocalClientId = ServerClientId; IsServer = true; IsClient = true; @@ -1249,12 +1251,12 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A } } - public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds) + public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds, bool serverCanSendToServerId = false) where T : INetworkMessage where U : IReadOnlyList { // Prevent server sending to itself - if (IsServer) + if (IsServer && !serverCanSendToServerId) { ulong* nonServerIds = stackalloc ulong[clientIds.Count]; int newIdx = 0; @@ -1278,11 +1280,11 @@ public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U } public unsafe int SendMessage(in T message, NetworkDelivery delivery, - ulong* clientIds, int numClientIds) + ulong* clientIds, int numClientIds, bool serverCanSendToServerId = false) where T: INetworkMessage { // Prevent server sending to itself - if (IsServer) + if (IsServer && !serverCanSendToServerId) { ulong* nonServerIds = stackalloc ulong[numClientIds]; int newIdx = 0; @@ -1306,11 +1308,11 @@ public unsafe int SendMessage(in T message, NetworkDelivery delivery, return m_MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds); } - public int SendMessage(in T message, NetworkDelivery delivery, ulong clientId) + public int SendMessage(in T message, NetworkDelivery delivery, ulong clientId, bool serverCanSendToServerId = false) where T: INetworkMessage { // Prevent server sending to itself - if (IsServer && clientId == ServerClientId) + if (IsServer && clientId == ServerClientId && !serverCanSendToServerId) { return 0; } @@ -1479,21 +1481,24 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? }; if (!NetworkConfig.EnableSceneManagement) { - if (SpawnManager.SpawnedObjectsList.Count == 0) + if (SpawnManager.SpawnedObjectsList.Count != 0) { - return; + message.SceneObjectCount = SpawnManager.SpawnedObjectsList.Count; + message.SpawnedObjectsList = SpawnManager.SpawnedObjectsList; } + } + + SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId); - message.SceneObjectCount = SpawnManager.SpawnedObjectsList.Count; - message.SpawnedObjectsList = SpawnManager.SpawnedObjectsList; + // If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization + if (!NetworkConfig.EnableSceneManagement) + { InvokeOnClientConnectedCallback(ownerClientId); } - // If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization else { SceneManager.SynchronizeNetworkObjects(ownerClientId); } - SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId); } else // Server just adds itself as an observer to all spawned NetworkObjects { @@ -1537,6 +1542,11 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) { ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key) }; + message.ObjectInfo.Metadata.Hash = playerPrefabHash; + message.ObjectInfo.Metadata.IsSceneObject = false; + message.ObjectInfo.Metadata.HasParent = false; + message.ObjectInfo.Metadata.IsPlayerObject = true; + message.ObjectInfo.Metadata.OwnerClientId = clientId; SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 53a20fd76d..b5f3726957 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1102,6 +1102,28 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, ref Fas networkObject?.SetNetworkParenting(sceneObject.Metadata.IsReparented, sceneObject.LatestParent); + if (sceneObject.Metadata.HasNetworkVariables) + { + if (networkObject == null) + { + // Log the error that the NetworkObject failed to construct + Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Metadata.Hash}."); + + // If we failed to load this NetworkObject, then skip past the network variable data + variableData.ReadValueSafe(out ushort varSize); + variableData.Seek(variableData.Position + varSize); + + variableData.ReadValueSafe(out ushort magic); + if (magic != (ushort) 0x12AB) + { + NetworkLog.LogWarning($"Var data ended not on the magic value."); + } + + // We have nothing left to do here. + return null; + } + } + // Spawn the NetworkObject networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, ref variableData, false); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index 03acb8ef41..f6859f6a5d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.IO; -using PlasticGui.Configuration; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Messages; -using UnityEditor.VersionControl; using UnityEngine; namespace Unity.Netcode @@ -409,6 +406,7 @@ internal void ReadIndex(in SnapshotDataMessage message) fixed (byte* buffer = RecvBuffer) { var reader = new FastBufferReader(buffer, Collections.Allocator.None, RecvBuffer.Length); +#pragma warning disable CS0728 // Warns that reader may be reassigned within ReadDelta, but ReadDelta does not reassign it. using (reader) { reader.Seek(Entries[pos].Position); @@ -417,6 +415,7 @@ internal void ReadIndex(in SnapshotDataMessage message) // Not using keepDirtyDelta anymore which is great. todo: remove and check for the overall effect on > 2 player networkVariable.ReadDelta(ref reader, false); } +#pragma warning restore CS0728 // Warns that reader may be reassigned within ReadDelta, but ReadDelta does not reassign it. } } } @@ -946,6 +945,7 @@ private unsafe void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBa { // write var into buffer, possibly adjusting entry's position and Length var varBuffer = new FastBufferWriter(1300, Allocator.Temp); +#pragma warning disable CS0728 // Warns that varBuffer may be reassigned within ReadDelta, but ReadDelta does not reassign it. using (varBuffer) { networkVariable.WriteDelta(ref varBuffer); @@ -960,6 +960,7 @@ private unsafe void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBa UnsafeUtility.MemCpy(buffer + snapshot.Entries[index].Position, varBuffer.GetUnsafePtr(), varBuffer.Length); } } +#pragma warning restore CS0728 // Warns that varBuffer may be reassigned within ReadDelta, but ReadDelta does not reassign it. } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index 834300d0c3..5502ed440e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -32,7 +32,16 @@ internal CustomMessagingManager(NetworkManager networkManager) internal void InvokeUnnamedMessage(ulong clientId, ref FastBufferReader reader) { - OnUnnamedMessage?.Invoke(clientId, ref reader); + if (OnUnnamedMessage != null) + { + var pos = reader.Position; + var delegates = OnUnnamedMessage.GetInvocationList(); + foreach (var handler in delegates) + { + reader.Seek(pos); + ((UnnamedMessageDelegate)handler).Invoke(clientId, ref reader); + } + } m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, reader.Length); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs index 02de971d14..0946be0cb2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -7,7 +7,7 @@ internal struct ChangeOwnershipMessage: INetworkMessage public void Serialize(ref FastBufferWriter writer) { - writer.WriteValue(this); + writer.WriteValueSafe(this); } public static void Receive(ref FastBufferReader reader, NetworkContext context) @@ -17,7 +17,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) { return; } - reader.ReadValue(out ChangeOwnershipMessage message); + reader.ReadValueSafe(out ChangeOwnershipMessage message); message.Handle(context.SenderId, networkManager, reader.Length); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs index e7b1616d7a..4972771c99 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs @@ -1,7 +1,5 @@ using System; using System.Runtime.InteropServices; -using UnityEditor.VersionControl; -using UnityEngine; namespace Unity.Netcode.Messages { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs index b256dd3c6a..c4ad3369d5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs @@ -109,6 +109,12 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) } var message = new NetworkVariableDeltaMessage(); + if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.NetworkObjectId) + + FastBufferWriter.GetWriteSize(message.NetworkBehaviourIndex))) + { + throw new OverflowException( + $"Not enough data in the buffer to read {nameof(NetworkVariableDeltaMessage)}"); + } reader.ReadValue(out message.NetworkObjectId); reader.ReadValue(out message.NetworkBehaviourIndex); message.Handle(context.SenderId, ref reader, networkManager); @@ -135,7 +141,7 @@ public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager n if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - reader.ReadValue(out varSize); + reader.ReadValueSafe(out varSize); if (varSize == 0) { @@ -144,7 +150,7 @@ public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager n } else { - reader.ReadValue(out bool deltaExists); + reader.ReadValueSafe(out bool deltaExists); if (!deltaExists) { continue; diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs index 2e3f5b7040..e5e00982ba 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs @@ -16,14 +16,14 @@ public struct ParentSyncMessage : INetworkMessage public void Serialize(ref FastBufferWriter writer) { - writer.WriteValue(NetworkObjectId); - writer.WriteValue(IsReparented); + writer.WriteValueSafe(NetworkObjectId); + writer.WriteValueSafe(IsReparented); if (IsReparented) { - writer.WriteValue(IsLatestParentSet); + writer.WriteValueSafe(IsLatestParentSet); if (IsLatestParentSet) { - writer.WriteValue((ulong)LatestParent); + writer.WriteValueSafe((ulong)LatestParent); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index fe3f506cc7..be8b621a07 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -132,7 +132,7 @@ public MessagingSystem(IMessageSender messageSender, object owner, ulong localCl RegisterMessageType(type); } } - catch(Exception e) + catch(Exception) { Dispose(); throw; @@ -223,7 +223,6 @@ internal void HandleIncomingData(ulong clientId, ArraySegment data, float for (var messageIdx = 0; messageIdx < batchHeader.BatchSize; ++messageIdx) { - Debug.Log($"Receiving a batch: {BitConverter.ToString(batchReader.ToArray())}"); if (!batchReader.TryBeginRead(sizeof(MessageHeader))) { NetworkLog.LogWarning("Received a batch that didn't have enough data for all of its batches, ending early!"); @@ -288,8 +287,8 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, { m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length); } - Debug.Log($"Processing {type} ({header.MessageType}): {BitConverter.ToString(reader.ToArray())}"); var handler = m_MessageHandlers[header.MessageType]; +#pragma warning disable CS0728 // Warns that reader may be reassigned within the handler, but the handler does not reassign it. using (reader) { // No user-land message handler exceptions should escape the receive loop. @@ -306,6 +305,7 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, Debug.LogError(e); } } +#pragma warning restore CS0728 // Warns that reader may be reassigned within the handler, but the handler does not reassign it. for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { m_Hooks[hookIdx].OnAfterReceiveMessage(senderId, type, reader.Length); @@ -360,6 +360,7 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in { var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? 64000 : 1300; var tmpSerializer = new FastBufferWriter(1300, Allocator.Temp, maxSize); +#pragma warning disable CS0728 // Warns that tmpSerializer may be reassigned within Serialize, but Serialize does not reassign it. using (tmpSerializer) { message.Serialize(ref tmpSerializer); @@ -377,9 +378,7 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in { m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(T), delivery); } - - Debug.Log($"Sending {typeof(T)} ({m_MessageTypes[typeof(T)]}) to {clientId}: {BitConverter.ToString(tmpSerializer.ToArray())}"); - + ref var sendQueueItem = ref m_SendQueues[clientId].Value; if (sendQueueItem.Count == 0) { @@ -427,9 +426,8 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in { m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(T), delivery, tmpSerializer.Length + sizeof(MessageHeader)); } - Debug.Log($"Sented {typeof(T)} to {clientId} batch size is now {writeQueueItem.BatchHeader.BatchSize}"); - Debug.Log(BitConverter.ToString(writeQueueItem.Writer.ToArray())); } +#pragma warning restore CS0728 // Warns that tmpSerializer may be reassigned within Serialize, but Serialize does not reassign it. return tmpSerializer.Length; } @@ -477,8 +475,7 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, return SendMessage(message, delivery, new PointerListWrapper(clientIds, numClientIds)); } - internal unsafe int SendMessage(in T message, NetworkDelivery delivery, - ulong clientId) + internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong clientId) where T: INetworkMessage { ulong* clientIds = stackalloc ulong[] {clientId}; @@ -506,10 +503,11 @@ internal void ProcessSendQueues() } queueItem.Writer.Seek(0); +#if UNITY_EDITOR || DEVELOPMENT_BUILD // Skipping the Verify and sneaking the write mark in because we know it's fine. queueItem.Writer.AllowedWriteMark = 2; +#endif queueItem.Writer.WriteValue(queueItem.BatchHeader); - Debug.Log($"Sending a batch to {clientId}: {BitConverter.ToString(queueItem.Writer.ToArray())}"); m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); queueItem.Writer.Dispose(); diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs index aa1e7c3c5e..ac3933226b 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs @@ -65,16 +65,16 @@ public override void ResetDirty() /// public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { - reader.ReadValue(out ushort deltaCount); + reader.ReadValueSafe(out ushort deltaCount); for (int i = 0; i < deltaCount; i++) { - reader.ReadValue(out NetworkDictionaryEvent.EventType eventType); + reader.ReadValueSafe(out NetworkDictionaryEvent.EventType eventType); switch (eventType) { case NetworkDictionaryEvent.EventType.Add: { - reader.ReadValue(out TKey key); - reader.ReadValue(out TValue value); + reader.ReadValueSafe(out TKey key); + reader.ReadValueSafe(out TValue value); m_Dictionary.Add(key, value); if (OnDictionaryChanged != null) @@ -100,7 +100,7 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkDictionaryEvent.EventType.Remove: { - reader.ReadValue(out TKey key); + reader.ReadValueSafe(out TKey key); TValue value; m_Dictionary.TryGetValue(key, out value); m_Dictionary.Remove(key); @@ -128,8 +128,8 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkDictionaryEvent.EventType.RemovePair: { - reader.ReadValue(out TKey key); - reader.ReadValue(out TValue value); + reader.ReadValueSafe(out TKey key); + reader.ReadValueSafe(out TValue value); m_Dictionary.Remove(new KeyValuePair(key, value)); if (OnDictionaryChanged != null) @@ -177,8 +177,8 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkDictionaryEvent.EventType.Value: { - reader.ReadValue(out TKey key); - reader.ReadValue(out TValue value); + reader.ReadValueSafe(out TKey key); + reader.ReadValueSafe(out TValue value); m_Dictionary[key] = value; @@ -211,11 +211,11 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) public override void ReadField(ref FastBufferReader reader) { m_Dictionary.Clear(); - reader.ReadValue(out ushort entryCount); + reader.ReadValueSafe(out ushort entryCount); for (int i = 0; i < entryCount; i++) { - reader.ReadValue(out TKey key); - reader.ReadValue(out TValue value); + reader.ReadValueSafe(out TKey key); + reader.ReadValueSafe(out TValue value); m_Dictionary.Add(key, value); } } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index 5d9c2ae72f..90d1ca58d0 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -71,37 +71,37 @@ public override bool IsDirty() /// public override void WriteDelta(ref FastBufferWriter writer) { - writer.WriteValue((ushort)m_DirtyEvents.Count); + writer.WriteValueSafe((ushort)m_DirtyEvents.Count); for (int i = 0; i < m_DirtyEvents.Count; i++) { - writer.WriteValue(m_DirtyEvents[i].Type); + writer.WriteValueSafe(m_DirtyEvents[i].Type); switch (m_DirtyEvents[i].Type) { case NetworkListEvent.EventType.Add: { - writer.WriteValue(m_DirtyEvents[i].Value); + writer.WriteValueSafe(m_DirtyEvents[i].Value); } break; case NetworkListEvent.EventType.Insert: { - writer.WriteValue(m_DirtyEvents[i].Index); - writer.WriteValue(m_DirtyEvents[i].Value); + writer.WriteValueSafe(m_DirtyEvents[i].Index); + writer.WriteValueSafe(m_DirtyEvents[i].Value); } break; case NetworkListEvent.EventType.Remove: { - writer.WriteValue(m_DirtyEvents[i].Value); + writer.WriteValueSafe(m_DirtyEvents[i].Value); } break; case NetworkListEvent.EventType.RemoveAt: { - writer.WriteValue(m_DirtyEvents[i].Index); + writer.WriteValueSafe(m_DirtyEvents[i].Index); } break; case NetworkListEvent.EventType.Value: { - writer.WriteValue(m_DirtyEvents[i].Index); - writer.WriteValue(m_DirtyEvents[i].Value); + writer.WriteValueSafe(m_DirtyEvents[i].Index); + writer.WriteValueSafe(m_DirtyEvents[i].Value); } break; case NetworkListEvent.EventType.Clear: @@ -116,10 +116,10 @@ public override void WriteDelta(ref FastBufferWriter writer) /// public override void WriteField(ref FastBufferWriter writer) { - writer.WriteValue((ushort)m_List.Count); + writer.WriteValueSafe((ushort)m_List.Count); for (int i = 0; i < m_List.Count; i++) { - writer.WriteValue(m_List[i]); + writer.WriteValueSafe(m_List[i]); } } @@ -127,10 +127,10 @@ public override void WriteField(ref FastBufferWriter writer) public override void ReadField(ref FastBufferReader reader) { m_List.Clear(); - reader.ReadValue(out ushort count); + reader.ReadValueSafe(out ushort count); for (int i = 0; i < count; i++) { - reader.ReadValue(out T value); + reader.ReadValueSafe(out T value); m_List.Add(value); } } @@ -138,15 +138,15 @@ public override void ReadField(ref FastBufferReader reader) /// public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { - reader.ReadValue(out ushort deltaCount); + reader.ReadValueSafe(out ushort deltaCount); for (int i = 0; i < deltaCount; i++) { - reader.ReadValue(out NetworkListEvent.EventType eventType); + reader.ReadValueSafe(out NetworkListEvent.EventType eventType); switch (eventType) { case NetworkListEvent.EventType.Add: { - reader.ReadValue(out T value); + reader.ReadValueSafe(out T value); m_List.Add(value); if (OnListChanged != null) @@ -172,8 +172,8 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkListEvent.EventType.Insert: { - reader.ReadValue(out int index); - reader.ReadValue(out T value); + reader.ReadValueSafe(out int index); + reader.ReadValueSafe(out T value); m_List.Insert(index, value); if (OnListChanged != null) @@ -199,7 +199,7 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkListEvent.EventType.Remove: { - reader.ReadValue(out T value); + reader.ReadValueSafe(out T value); int index = m_List.IndexOf(value); m_List.RemoveAt(index); @@ -226,7 +226,7 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkListEvent.EventType.RemoveAt: { - reader.ReadValue(out int index); + reader.ReadValueSafe(out int index); T value = m_List[index]; m_List.RemoveAt(index); @@ -253,8 +253,8 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkListEvent.EventType.Value: { - reader.ReadValue(out int index); - reader.ReadValue(out T value); + reader.ReadValueSafe(out int index); + reader.ReadValueSafe(out T value); if (index < m_List.Count) { m_List[index] = value; diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs index 9f8bc01667..85c280ae91 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs @@ -71,32 +71,32 @@ public override bool IsDirty() /// public override void WriteField(ref FastBufferWriter writer) { - writer.WriteValue((ushort)m_Set.Count); + writer.WriteValueSafe((ushort)m_Set.Count); foreach (T value in m_Set) { - writer.WriteValue(value); + writer.WriteValueSafe(value); } } /// public override void WriteDelta(ref FastBufferWriter writer) { - writer.WriteValue((ushort)m_DirtyEvents.Count); + writer.WriteValueSafe((ushort)m_DirtyEvents.Count); for (int i = 0; i < m_DirtyEvents.Count; i++) { - writer.WriteValue(m_DirtyEvents[i].Type); + writer.WriteValueSafe(m_DirtyEvents[i].Type); switch (m_DirtyEvents[i].Type) { case NetworkSetEvent.EventType.Add: { - writer.WriteValue(m_DirtyEvents[i].Value); + writer.WriteValueSafe(m_DirtyEvents[i].Value); } break; case NetworkSetEvent.EventType.Remove: { - writer.WriteValue(m_DirtyEvents[i].Value); + writer.WriteValueSafe(m_DirtyEvents[i].Value); } break; case NetworkSetEvent.EventType.Clear: @@ -112,11 +112,11 @@ public override void WriteDelta(ref FastBufferWriter writer) public override void ReadField(ref FastBufferReader reader) { m_Set.Clear(); - reader.ReadValue(out ushort count); + reader.ReadValueSafe(out ushort count); for (int i = 0; i < count; i++) { - reader.ReadValue(out T obj); + reader.ReadValueSafe(out T obj); m_Set.Add(obj); } } @@ -124,15 +124,15 @@ public override void ReadField(ref FastBufferReader reader) /// public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { - reader.ReadValue(out ushort deltaCount); + reader.ReadValueSafe(out ushort deltaCount); for (int i = 0; i < deltaCount; i++) { - reader.ReadValue(out NetworkSetEvent.EventType eventType); + reader.ReadValueSafe(out NetworkSetEvent.EventType eventType); switch (eventType) { case NetworkSetEvent.EventType.Add: { - reader.ReadValue(out T value); + reader.ReadValueSafe(out T value); m_Set.Add(value); if (OnSetChanged != null) @@ -156,7 +156,7 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) break; case NetworkSetEvent.EventType.Remove: { - reader.ReadValue(out T value); + reader.ReadValueSafe(out T value); m_Set.Remove(value); if (OnSetChanged != null) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs index 3721af4dc9..c10394a81b 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs @@ -113,7 +113,7 @@ public override void WriteDelta(ref FastBufferWriter writer) public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { T previousValue = m_InternalValue; - reader.ReadValue(out m_InternalValue); + reader.ReadValueSafe(out m_InternalValue); if (keepDirtyDelta) { @@ -132,7 +132,7 @@ public override void ReadField(ref FastBufferReader reader) /// public override void WriteField(ref FastBufferWriter writer) { - writer.WriteValue(m_InternalValue); + writer.WriteValueSafe(m_InternalValue); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 162ce92e41..2ad988915f 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -475,7 +475,7 @@ internal void Deserialize(ref FastBufferReader reader) // We store off the trailing in-scene placed serialized NetworkObject data to // be processed once we are done loading. m_HasInternalBuffer = true; - InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, reader.Length + reader.Position); + InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, reader.Length - reader.Position); } break; } @@ -531,7 +531,7 @@ internal void DeserializeScenePlacedObjects() try { // is not packed! - InternalBuffer.ReadValueSafe(out uint newObjectsCount); + InternalBuffer.ReadValueSafe(out ushort newObjectsCount); for (ushort i = 0; i < newObjectsCount; i++) { @@ -738,7 +738,7 @@ internal void ReadSceneEventProgressDone(ref FastBufferReader reader) ClientsCompleted = new List(); for (int i = 0; i < completedCount; i++) { - reader.ReadValue(out ulong clientId); + reader.ReadValueSafe(out ulong clientId); ClientsCompleted.Add(clientId); } @@ -746,7 +746,7 @@ internal void ReadSceneEventProgressDone(ref FastBufferReader reader) ClientsTimedOut = new List(); for (int i = 0; i < timedOutCount; i++) { - reader.ReadValue(out ulong clientId); + reader.ReadValueSafe(out ulong clientId); ClientsTimedOut.Add(clientId); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index e08df4c8d3..92c974d1e6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -523,13 +523,13 @@ public unsafe void ReadValueSafe(out T[] array) where T : unmanaged if (!TryBeginReadInternal(sizeof(int))) { - throw new OverflowException("Writing past the end of the buffer"); + throw new OverflowException("Reading past the end of the buffer"); } ReadValue(out int sizeInTs); int sizeInBytes = sizeInTs * sizeof(T); if (!TryBeginReadInternal(sizeInBytes)) { - throw new OverflowException("Writing past the end of the buffer"); + throw new OverflowException("Reading past the end of the buffer"); } array = new T[sizeInTs]; fixed (T* native = array) diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs index 13b56027cc..38d093caf4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs @@ -48,7 +48,7 @@ private unsafe void Resize() { m_Capacity *= 2; var data = (T*)UnsafeUtility.Malloc(m_Capacity * sizeof(T), UnsafeUtility.AlignOf(), m_Allocator); - UnsafeUtility.MemCpy(data, m_Data, m_Length); + UnsafeUtility.MemCpy(data, m_Data, m_Length*sizeof(T)); UnsafeUtility.Free(m_Data, m_Allocator); m_Data = data; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Components/BufferDataValidationComponent.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Components/BufferDataValidationComponent.cs index 90f7c515b2..759cce10b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Components/BufferDataValidationComponent.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Components/BufferDataValidationComponent.cs @@ -18,7 +18,7 @@ public class BufferDataValidationComponent : NetworkBehaviour /// /// The maximum size of the buffer to send /// - public int MaximumBufferSize = 1 << 20; + public int MaximumBufferSize = 1 << 15; /// /// The rate at which the buffer size increases until it reaches MaximumBufferSize diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs index 5b1191e39b..55bc79bed0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs @@ -21,7 +21,7 @@ public class NamedMessageTests : BaseMultiInstanceTest public IEnumerator NamedMessageIsReceivedOnClientWithContent() { var messageName = Guid.NewGuid().ToString(); - var messageContent = Guid.NewGuid().ToString(); + var messageContent = Guid.NewGuid(); var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { @@ -53,9 +53,7 @@ public IEnumerator NamedMessageIsReceivedOnClientWithContent() public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent() { var messageName = Guid.NewGuid().ToString(); - var messageContent = Guid.NewGuid().ToString(); - using var messageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageContent)); - + var messageContent = Guid.NewGuid(); var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs index 109fb01e46..57261d544a 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs @@ -20,7 +20,7 @@ public class UnnamedMessageTests : BaseMultiInstanceTest [UnityTest] public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() { - var messageContent = Guid.NewGuid().ToString(); + var messageContent = Guid.NewGuid(); var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { @@ -31,7 +31,7 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() } ulong receivedMessageSender = 0; - string receivedMessageContent = null; + Guid receivedMessageContent; FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { @@ -42,7 +42,6 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() yield return new WaitForSeconds(0.2f); - Assert.NotNull(receivedMessageContent); Assert.AreEqual(messageContent, receivedMessageContent); Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender); } @@ -50,7 +49,7 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() [UnityTest] public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() { - var messageContent = Guid.NewGuid().ToString(); + var messageContent = Guid.NewGuid(); var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { @@ -61,7 +60,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() } ulong firstReceivedMessageSender = 0; - string firstReceivedMessageContent = null; + Guid firstReceivedMessageContent; FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { @@ -71,22 +70,20 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() }; ulong secondReceivedMessageSender = 0; - string secondReceivedMessageContent = null; + Guid secondReceivedMessageContent; SecondClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { secondReceivedMessageSender = sender; - reader.ReadValueSafe(out firstReceivedMessageContent); + reader.ReadValueSafe(out secondReceivedMessageContent); }; yield return new WaitForSeconds(0.2f); - Assert.NotNull(firstReceivedMessageContent); Assert.AreEqual(messageContent, firstReceivedMessageContent); Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender); - Assert.NotNull(secondReceivedMessageContent); Assert.AreEqual(messageContent, secondReceivedMessageContent); Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index b4067dcaa1..4eb22f3582 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -19,15 +19,13 @@ public void NetworkObjectSceneSerializationFailure() { var networkObjectsToTest = new List(); - var writer = new FastBufferWriter(1300, Allocator.Temp, 65536); + var writer = new FastBufferWriter(1300, Allocator.Temp, 4096000); var invalidNetworkObjectOffsets = new List(); var invalidNetworkObjectIdCount = new List(); var invalidNetworkObjects = new List(); var invalidNetworkObjectFrequency = 3; using (writer) { - - // Construct 50 NetworkObjects for (int i = 0; i < 50; i++) { @@ -55,13 +53,14 @@ public void NetworkObjectSceneSerializationFailure() invalidNetworkObjects.Add(gameObject); - writer.WriteValue((int) networkObject.gameObject.scene.handle); + writer.WriteValueSafe((int) networkObject.gameObject.scene.handle); // Serialize the invalid NetworkObject var sceneObject = networkObject.GetMessageSceneObject(0); + var prePosition = writer.Position; sceneObject.Serialize(ref writer); Debug.Log( - $"Invalid {nameof(NetworkObject)} Size {writer.Position - invalidNetworkObjectOffsets[invalidNetworkObjectOffsets.Count - 1]}"); + $"Invalid {nameof(NetworkObject)} Size {writer.Position - prePosition}"); // Now adjust how frequent we will inject invalid NetworkObjects invalidNetworkObjectFrequency = Random.Range(2, 5); @@ -85,7 +84,7 @@ public void NetworkObjectSceneSerializationFailure() networkObjectsToTest.Add(gameObject); - writer.WriteValue((int) networkObject.gameObject.scene.handle); + writer.WriteValueSafe((int) networkObject.gameObject.scene.handle); // Handle populating the scenes loaded list var scene = networkObject.gameObject.scene; @@ -151,7 +150,7 @@ public void NetworkObjectSceneSerializationFailure() } - reader.ReadValue(out int handle); + reader.ReadValueSafe(out int handle); NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(handle); var sceneObject = new NetworkObject.SceneObject(); sceneObject.Deserialize(ref reader); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs index 0796629872..a2b8ba39ac 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs @@ -28,6 +28,7 @@ public override bool IsDirty() public override void WriteDelta(ref FastBufferWriter writer) { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(k_DummyValue) + 1); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits((byte)1, 1); @@ -39,6 +40,7 @@ public override void WriteDelta(ref FastBufferWriter writer) public override void WriteField(ref FastBufferWriter writer) { + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(k_DummyValue) + 1); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits((byte)1, 1); @@ -50,6 +52,7 @@ public override void WriteField(ref FastBufferWriter writer) public override void ReadField(ref FastBufferReader reader) { + reader.TryBeginRead(FastBufferWriter.GetWriteSize(k_DummyValue) + 1); using (var bitReader = reader.EnterBitwiseContext()) { bitReader.ReadBits(out byte b, 1); @@ -63,6 +66,7 @@ public override void ReadField(ref FastBufferReader reader) public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { + reader.TryBeginRead(FastBufferWriter.GetWriteSize(k_DummyValue) + 1); using (var bitReader = reader.EnterBitwiseContext()) { bitReader.ReadBits(out byte b, 1); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs index 64ef998118..d9390d71f6 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs @@ -57,16 +57,6 @@ public IEnumerator BufferDataValidation() Assert.IsTrue(testsAreComplete); } - /// - /// This tests the RpcQueueContainer and RpcQueueHistoryFrame - /// ***NOTE: We want to run this test always LAST! - /// - [Test, Order(3)] - public void RpcQueueContainerClass() - { - } - - [TearDown] public void TearDown() { diff --git a/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs b/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs index 694ae3724b..9aeb89bf8f 100644 --- a/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs +++ b/testproject/Assets/Tests/Manual/DontDestroyOnLoad/ObjectToNotDestroyBehaviour.cs @@ -85,7 +85,7 @@ private IEnumerator SendContinualPing() { m_PingCounter++; PingUpdateClientRpc(m_PingCounter); - yield return new WaitForSeconds(1); + yield return new WaitForSeconds(0.1f); } yield return null; } diff --git a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs index 31e218be28..f81a6a87c9 100644 --- a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs +++ b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs @@ -4,26 +4,6 @@ namespace TestProject.ManualTests { - class Foo - { - public int I; - } - - static class FastBufferWriterExtensions - { - public static void WriteValueSafe(this ref FastBufferWriter writer, in Foo value) - { - writer.WriteValueSafe(value.I); - } - - - public static void ReadValueSafe(this ref FastBufferReader reader, out Foo value) - { - value = new Foo(); - reader.ReadValueSafe(out value.I); - } - - } /// /// Used with GenericObjects to randomly move them around @@ -102,68 +82,7 @@ private void ChangeDirectionClientRpc(Vector3 direction) { m_Direction = direction; } - private void ChangeDirectionClientRpcManual(Vector3 direction) - { - //IL_00d1: Unknown result type (might be due to invalid IL or missing references) - //IL_00d2: Unknown result type (might be due to invalid IL or missing references) - NetworkManager networkManager = base.NetworkManager; - if (networkManager == null || !networkManager.IsListening) - { - return; - } - if (__rpc_exec_stage != __RpcExecStage.Client && (networkManager.IsServer || networkManager.IsHost)) - { - FastBufferWriter writer = new FastBufferWriter(1300, (Allocator)2, 1300); - try - { - writer.WriteValueSafe(in direction); - ClientRpcParams sendParams = default(ClientRpcParams); - SendClientRpc(ref writer, 4099941514u, sendParams, RpcDelivery.Reliable); - } - finally - { - writer.Dispose(); - } - } - if (__rpc_exec_stage == __RpcExecStage.Client && (networkManager.IsClient || networkManager.IsHost)) - { - m_Direction = direction; - } - } - - [ClientRpc] - private void ChangeDirectionClientRpc(Foo testFoo) - { - } - private void ChangeDirectionClientRpcManual(NetworkBehaviour testFoo) - { - //IL_00d1: Unknown result type (might be due to invalid IL or missing references) - //IL_00d2: Unknown result type (might be due to invalid IL or missing references) - NetworkManager networkManager = base.NetworkManager; - if (networkManager == null || !networkManager.IsListening) - { - return; - } - if (__rpc_exec_stage != __RpcExecStage.Client && (networkManager.IsServer || networkManager.IsHost)) - { - FastBufferWriter writer = new FastBufferWriter(1300, (Allocator)2, 1300); - try - { - writer.WriteValueSafe(testFoo); - ClientRpcParams sendParams = default(ClientRpcParams); - SendClientRpc(ref writer, 4099941514u, sendParams, RpcDelivery.Reliable); - } - finally - { - writer.Dispose(); - } - } - if (__rpc_exec_stage == __RpcExecStage.Client && (networkManager.IsClient || networkManager.IsHost)) - { - } - } - - + private void OnCollisionStay(Collision collision) { if (IsServer) diff --git a/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs b/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs index 8c1fdcd944..7bd3b8f828 100644 --- a/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs +++ b/testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs @@ -121,8 +121,8 @@ public IEnumerator ValidateNetworkObjectSynchronization([Values(true, false)] bo { Assert.IsTrue(spawnedObject.gameObject.scene.name == "DontDestroyOnLoad"); var objectToNotDestroyBehaviour = spawnedObject.gameObject.GetComponent(); - Assert.IsTrue(objectToNotDestroyBehaviour.CurrentPing > 0); - Assert.IsTrue(objectToNotDestroyBehaviour.CurrentPing == serverobjectToNotDestroyBehaviour.CurrentPing); + Assert.Greater(objectToNotDestroyBehaviour.CurrentPing, 0); + Assert.AreEqual(serverobjectToNotDestroyBehaviour.CurrentPing, objectToNotDestroyBehaviour.CurrentPing); } } } diff --git a/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs b/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs deleted file mode 100644 index edab517e84..0000000000 --- a/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Collections; -using Unity.Netcode; -using Unity.Netcode.RuntimeTests; -using NUnit.Framework; -using TestProject.RuntimeTests.Support; -using UnityEngine; -using UnityEngine.TestTools; - -namespace TestProject.RuntimeTests -{ - public class FixedUpdateMessagesAreOnlyProcessedOnceTest - { - private GameObject m_Prefab; - private int m_OriginalFrameRate; - - - [UnityTearDown] - public IEnumerator Teardown() - { - // Shutdown and clean up both of our NetworkManager instances - if (m_Prefab) - { - MultiInstanceHelpers.Destroy(); - Object.Destroy(m_Prefab); - m_Prefab = null; - SpawnRpcDespawn.ClientUpdateCount = 0; - SpawnRpcDespawn.ServerUpdateCount = 0; - Application.targetFrameRate = m_OriginalFrameRate; - } - yield break; - } - - [UnitySetUp] - public IEnumerator Setup() - { - m_OriginalFrameRate = Application.targetFrameRate; - yield break; - } - - [UnityTest] - public IEnumerator TestFixedUpdateMessagesAreOnlyProcessedOnce() - { - NetworkUpdateStage testStage = NetworkUpdateStage.FixedUpdate; - - // Must be 1 for this test. - const int numClients = 1; - Assert.True(MultiInstanceHelpers.Create(numClients, out NetworkManager server, out NetworkManager[] clients)); - m_Prefab = new GameObject("Object"); - m_Prefab.AddComponent(); - SpawnRpcDespawn.TestStage = testStage; - var networkObject = m_Prefab.AddComponent(); - - // Make it a prefab - MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObject); - var handler = new SpawnRpcDespawnInstanceHandler(networkObject.GlobalObjectIdHash); - foreach (var client in clients) - { - client.PrefabHandler.AddHandler(networkObject, handler); - } - - var validNetworkPrefab = new NetworkPrefab(); - validNetworkPrefab.Prefab = m_Prefab; - server.NetworkConfig.NetworkPrefabs.Add(validNetworkPrefab); - foreach (var client in clients) - { - client.NetworkConfig.NetworkPrefabs.Add(validNetworkPrefab); - } - - // Start the instances - if (!MultiInstanceHelpers.Start(true, server, clients)) - { - Debug.LogError("Failed to start instances"); - Assert.Fail("Failed to start instances"); - } - - // [Client-Side] Wait for a connection to the server - yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients, null, 512)); - - // [Host-Side] Check to make sure all clients are connected - yield return MultiInstanceHelpers.Run( - MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1, null, 512)); - - // Setting targetFrameRate to 1 will cause FixedUpdate to get called multiple times. - // We should only see ClientUpdateCount increment once because only one RPC is being sent. - Application.targetFrameRate = 1; - var serverObject = Object.Instantiate(m_Prefab, Vector3.zero, Quaternion.identity); - NetworkObject serverNetworkObject = serverObject.GetComponent(); - serverNetworkObject.NetworkManagerOwner = server; - SpawnRpcDespawn srdComponent = serverObject.GetComponent(); - srdComponent.Activate(); - - // Wait until all objects have spawned. - int expectedCount = SpawnRpcDespawn.ClientUpdateCount + 1; - const int maxFrames = 240; - var doubleCheckTime = Time.realtimeSinceStartup + 1.0f; - while (SpawnRpcDespawn.ClientUpdateCount < expectedCount) - { - if (Time.frameCount > maxFrames) - { - // This is here in the event a platform is running at a higher - // frame rate than expected - if (doubleCheckTime < Time.realtimeSinceStartup) - { - Assert.Fail("Did not successfully call all expected client RPCs"); - break; - } - } - var nextFrameNumber = Time.frameCount + 1; - yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); - } - - Assert.AreEqual(testStage, SpawnRpcDespawn.StageExecutedByReceiver); - Assert.AreEqual(SpawnRpcDespawn.ServerUpdateCount, SpawnRpcDespawn.ClientUpdateCount); - Assert.True(handler.WasSpawned); - var lastFrameNumber = Time.frameCount + 1; - yield return new WaitUntil(() => Time.frameCount >= lastFrameNumber); - Assert.True(handler.WasDestroyed); - } - } -} diff --git a/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs.meta b/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs.meta deleted file mode 100644 index 99b85a0f53..0000000000 --- a/testproject/Assets/Tests/Runtime/FixedUpdateMessagesAreOnlyProcessedOnceTest.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: f00a39af37a64b65b30bfd8ee23a825d -timeCreated: 1627075514 \ No newline at end of file diff --git a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs index ef8f955bf6..49871dba41 100644 --- a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs +++ b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs @@ -227,8 +227,8 @@ public IEnumerator ClientConnectedCallbackTest([Values(true, false)] bool enable // [Host-Side] Check to make sure all clients are connected yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1, null, 512)); - Assert.True(m_ClientConnectedInvocations == 3); - Assert.True(m_ServerClientConnectedInvocations == 4); + Assert.AreEqual(3, m_ClientConnectedInvocations); + Assert.AreEqual(4, m_ServerClientConnectedInvocations); } private void Client_OnClientConnectedCallback(ulong clientId) From 7a21615aa61520cdf78057894aacc0a7d4d25b0c Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 16:25:14 -0500 Subject: [PATCH 31/58] standards.py --fix --- .../Components/NetworkAnimator.cs | 12 +-- .../Components/NetworkTransform.cs | 2 +- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 68 ++++++++-------- .../CodeGen/RuntimeAccessModifiersILPP.cs | 2 +- .../Runtime/Core/NetworkBehaviour.cs | 13 ++- .../Runtime/Core/NetworkManager.cs | 35 ++++---- .../Runtime/Core/NetworkObject.cs | 49 +++++------ .../Runtime/Core/SnapshotSystem.cs | 8 +- .../Runtime/Messaging/BatchHeader.cs | 4 +- .../Runtime/Messaging/Bind.cs | 4 +- .../Runtime/Messaging/CustomMessageManager.cs | 1 - .../Runtime/Messaging/IMessageSender.cs | 4 +- .../Runtime/Messaging/INetworkHooks.cs | 8 +- .../Messages/ChangeOwnershipMessage.cs | 10 +-- .../Messages/ConnectionApprovedMessage.cs | 18 ++--- .../Messages/ConnectionRequestMessage.cs | 30 +++---- .../Messaging/Messages/CreateObjectMessage.cs | 8 +- .../Messages/DestroyObjectMessage.cs | 8 +- .../Messaging/Messages/NamedMessage.cs | 6 +- .../Messages/NetworkVariableDeltaMessage.cs | 18 ++--- .../Messaging/Messages/ParentSyncMessage.cs | 24 +++--- .../Runtime/Messaging/Messages/RpcMessage.cs | 7 +- .../Messaging/Messages/SceneEventMessage.cs | 6 +- .../Messaging/Messages/ServerLogMessage.cs | 8 +- .../Messaging/Messages/SnapshotDataMessage.cs | 28 +++---- .../Messaging/Messages/TimeSyncMessage.cs | 8 +- .../Messaging/Messages/UnnamedMessage.cs | 6 +- .../Runtime/Messaging/MessagingSystem.cs | 81 +++++++++---------- .../Runtime/Messaging/NetworkContext.cs | 4 +- .../Runtime/Metrics/MetricHooks.cs | 5 +- .../Collections/NetworkDictionary.cs | 1 - .../Collections/NetworkList.cs | 3 +- .../NetworkVariable/Collections/NetworkSet.cs | 19 +++-- .../NetworkVariable/NetworkVariable.cs | 3 +- .../NetworkVariable/NetworkVariableBase.cs | 2 - .../Runtime/Profiling/ProfilingHooks.cs | 15 ++-- .../SceneManagement/NetworkSceneManager.cs | 13 ++- .../Runtime/SceneManagement/SceneEventData.cs | 14 ++-- .../Runtime/Serialization/FastBufferReader.cs | 16 ++-- .../FastBufferReaderExtensions.cs | 6 +- .../Runtime/Serialization/FastBufferWriter.cs | 16 ++-- .../Serialization/INetworkSerializable.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 8 +- .../Runtime/Utility/DynamicUnmanagedArray.cs | 11 ++- .../Runtime/Utility/FixedUnmanagedArray.cs | 28 +++---- .../Tests/Editor/MessageBatcherTests.cs | 20 ----- .../Tests/Editor/MessageBatcherTests.cs.meta | 11 --- .../Editor/Messaging/MessageReceivingTests.cs | 44 +++++----- .../Messaging/MessageRegistrationTests.cs | 81 +++++++++---------- .../Editor/Messaging/MessageSendingTests.cs | 34 ++++---- .../Editor/Messaging/NopMessageSender.cs | 6 +- .../BaseFastBufferReaderWriterTest.cs | 2 - .../Editor/Serialization/BitReaderTests.cs | 40 ++++----- .../Editor/Serialization/BitWriterTests.cs | 46 +++++------ .../Serialization/BufferSerializerTests.cs | 2 - .../Serialization/FastBufferReaderTests.cs | 64 +++++---------- .../Serialization/FastBufferWriterTests.cs | 60 +++++--------- .../Runtime/Messaging/NamedMessageTests.cs | 2 - .../Runtime/Messaging/UnnamedMessageTests.cs | 8 +- .../NetworkObjectSceneSerializationTests.cs | 8 +- .../Tests/Runtime/NetworkVarBufferCopyTest.cs | 3 +- .../Tests/Runtime/NetworkVariableTests.cs | 6 +- .../Tests/Runtime/RpcQueueTests.cs | 3 +- .../Tests/Manual/Scripts/RandomMovement.cs | 5 +- .../Manual/Scripts/StatsInfoContainer.cs | 2 +- .../Assets/Tests/Runtime/MessageOrdering.cs | 1 - .../Tests/Runtime/RpcINetworkSerializable.cs | 5 +- 67 files changed, 477 insertions(+), 618 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs delete mode 100644 com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs index c8100d45a5..eb01597d64 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs @@ -76,7 +76,7 @@ public bool SetTrigger(int key) return TriggerParameters.Add(key); } - public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation { SerializeIntParameters(serializer); SerializeFloatParameters(serializer); @@ -85,7 +85,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T: IBuffer SerializeAnimatorLayerStates(serializer); } - private void SerializeAnimatorLayerStates(BufferSerializer serializer) where T: IBufferSerializerImplementation + private void SerializeAnimatorLayerStates(BufferSerializer serializer) where T : IBufferSerializerImplementation { int layerCount = serializer.IsReader ? 0 : LayerStates.Length; serializer.SerializeValue(ref layerCount); @@ -118,7 +118,7 @@ private void SerializeAnimatorLayerStates(BufferSerializer serializer) whe } } - private void SerializeTriggerParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation + private void SerializeTriggerParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation { int paramCount = serializer.IsReader ? 0 : TriggerParameters.Count; serializer.SerializeValue(ref paramCount); @@ -141,7 +141,7 @@ private void SerializeTriggerParameters(BufferSerializer serializer) where } } - private void SerializeBoolParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation + private void SerializeBoolParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation { int paramCount = serializer.IsReader ? 0 : BoolParameters.Count; serializer.SerializeValue(ref paramCount); @@ -167,7 +167,7 @@ private void SerializeBoolParameters(BufferSerializer serializer) where T: } } - private void SerializeFloatParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation + private void SerializeFloatParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation { int paramCount = serializer.IsReader ? 0 : FloatParameters.Count; serializer.SerializeValue(ref paramCount); @@ -193,7 +193,7 @@ private void SerializeFloatParameters(BufferSerializer serializer) where T } } - private void SerializeIntParameters(BufferSerializer serializer) where T: IBufferSerializerImplementation + private void SerializeIntParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation { int paramCount = serializer.IsReader ? 0 : IntParameters.Count; serializer.SerializeValue(ref paramCount); diff --git a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs index aa35fc91eb..b23991aabe 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs @@ -82,7 +82,7 @@ public bool HasScaleZ public float RotAngleX, RotAngleY, RotAngleZ; public float ScaleX, ScaleY, ScaleZ; - public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation { // InLocalSpace + HasXXX Bits serializer.SerializeValue(ref Bitset); diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 9a8f9510ee..65aab8b0d8 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -17,7 +17,7 @@ namespace Unity.Netcode.Editor.CodeGen { - + internal sealed class NetworkBehaviourILPP : ILPPInterface { public override ILPPInterface GetInstance() => this; @@ -92,7 +92,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) private ModuleDefinition m_MainModule; private PostProcessorAssemblyResolver m_AssemblyResolver; - + private MethodReference m_Debug_LogError_MethodRef; private TypeReference m_NetworkManager_TypeRef; private MethodReference m_NetworkManager_getLocalClientId_MethodRef; @@ -119,7 +119,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) private FieldReference m_ServerRpcParams_Receive_FieldRef; private FieldReference m_ServerRpcParams_Receive_SenderClientId_FieldRef; private TypeReference m_ClientRpcParams_TypeRef; - + private TypeReference m_FastBufferWriter_TypeRef; private MethodReference m_FastBufferWriter_Constructor; private MethodReference m_FastBufferWriter_Dispose; @@ -297,15 +297,15 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) m_FastBufferWriter_TypeRef = moduleDefinition.ImportReference(fastBufferWriterType); m_FastBufferWriter_Constructor = moduleDefinition.ImportReference( - fastBufferWriterType.GetConstructor(new[] {typeof(int), typeof(Allocator), typeof(int)})); + fastBufferWriterType.GetConstructor(new[] { typeof(int), typeof(Allocator), typeof(int) })); m_FastBufferWriter_Dispose = moduleDefinition.ImportReference(fastBufferWriterType.GetMethod("Dispose")); - - var FstBufferReaderType = typeof(FastBufferReader); - m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(FstBufferReaderType); - + + var fastBufferReaderType = typeof(FastBufferReader); + m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(fastBufferReaderType); + // Find all extension methods for FastBufferReader and FastBufferWriter to enable user-implemented // methods to be called. - List assemblies = new List(); + var assemblies = new List(); assemblies.Add(m_MainModule.Assembly); foreach (var reference in m_MainModule.AssemblyReferences) { @@ -314,7 +314,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) var extensionConstructor = moduleDefinition.ImportReference(typeof(ExtensionAttribute).GetConstructor(new Type[] { })); - foreach(var assembly in assemblies) + foreach (var assembly in assemblies) { foreach (var module in assembly.Modules) { @@ -333,7 +333,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) } var isExtension = false; - + foreach (var attr in method.CustomAttributes) { if (attr.Constructor.Resolve() == extensionConstructor.Resolve()) @@ -348,7 +348,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) } var parameters = method.Parameters; - + if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve()) { @@ -363,7 +363,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) } } } - + return true; } @@ -472,7 +472,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass var moduleName = m_MainModule.Name; if (moduleName.EndsWith(".dll") || moduleName.EndsWith(".exe")) { - moduleName = moduleName.Substring(0, moduleName.Length-4); + moduleName = moduleName.Substring(0, moduleName.Length - 4); } foreach (var reference in m_MainModule.AssemblyReferences) @@ -480,7 +480,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass var referenceName = reference.Name.Split(',')[0]; if (referenceName.EndsWith(".dll") || referenceName.EndsWith(".exe")) { - referenceName = referenceName.Substring(0, referenceName.Length-4); + referenceName = referenceName.Substring(0, referenceName.Length - 4); } if (moduleName == referenceName) @@ -568,7 +568,7 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio // serializer OR extension method exists for it. return rpcAttribute; } - + private MethodInfo GetWriteMethodViaSystemReflection(Type objectType, string name, Type paramType) { foreach (var method in objectType.GetMethods()) @@ -581,13 +581,13 @@ private MethodInfo GetWriteMethodViaSystemReflection(Type objectType, string nam { continue; } - + var checkType = paramType; if (paramType.IsArray) { checkType = paramType.GetElementType(); } - + if ((parameters[0].ParameterType == checkType) || (parameters[0].ParameterType == checkType.MakeByRefType() && parameters[0].IsIn) && parameters[0].ParameterType.IsArray == paramType.IsArray) { return method; @@ -653,7 +653,7 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer } } } - + var systemType = Type.GetType(paramType.FullName); if (systemType == null) { @@ -681,7 +681,7 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer return foundMethodRef; } - + private static MethodInfo GetReadMethodViaSystemReflection(Type objectType, string name, Type paramType) { foreach (var method in objectType.GetMethods()) @@ -693,13 +693,13 @@ private static MethodInfo GetReadMethodViaSystemReflection(Type objectType, stri { continue; } - + var checkType = paramType; if (paramType.IsArray) { checkType = paramType.GetElementType(); } - + if ((parameters[0].ParameterType == checkType.MakeByRefType() && parameters[0].IsOut) && parameters[0].ParameterType.IsArray == paramType.IsArray) { return method; @@ -740,7 +740,7 @@ private bool GetReadMethodForParameter(TypeReference paramType, out MethodRefere { var parameters = method.Resolve().Parameters; if ( - method.Name == "ReadValueSafe" + method.Name == "ReadValueSafe" && parameters[1].IsOut && parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() && ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray) @@ -750,7 +750,7 @@ private bool GetReadMethodForParameter(TypeReference paramType, out MethodRefere return true; } } - + var systemType = Type.GetType(paramType.FullName); if (systemType == null) { @@ -894,7 +894,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA // if (LogLevel.Normal > networkManager.LogLevel) instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx)); instructions.Add(processor.Create(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, (int) LogLevel.Normal)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)LogLevel.Normal)); instructions.Add(processor.Create(OpCodes.Cgt)); instructions.Add(processor.Create(OpCodes.Ldc_I4, 0)); instructions.Add(processor.Create(OpCodes.Ceq)); @@ -947,16 +947,16 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA m_Diagnostics.AddError(methodDefinition, $"Couldn't find boolean serializer! Something's wrong!"); return; } - + methodDefinition.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); int isSetLocalIndex = methodDefinition.Body.Variables.Count - 1; - + // bool isSet = (param != null); instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); instructions.Add(processor.Create(OpCodes.Ldnull)); instructions.Add(processor.Create(OpCodes.Cgt_Un)); instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex)); - + // writer.WriteValueSafe(isSet); instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex)); @@ -1084,15 +1084,15 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA // TODO: Figure out why try/catch here cause the try block not to execute at all. // End try block //instructions.Add(processor.Create(OpCodes.Leave, lastInstr)); - + // writer.Dispose(); var handlerFirst = processor.Create(OpCodes.Ldloca, serializerLocIdx); instructions.Add(handlerFirst); instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Dispose)); - + // End finally block //instructions.Add(processor.Create(OpCodes.Endfinally)); - + // try { ... serialization code ... } finally { writer.Dispose(); } /*var handler = new ExceptionHandler(ExceptionHandlerType.Finally) { @@ -1265,18 +1265,18 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition { m_Diagnostics.AddError(methodDefinition, $"Couldn't find boolean deserializer! Something's wrong!"); } - + // reader.ReadValueSafe(out bool isSet) nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean)); int isSetLocalIndex = nhandler.Body.Variables.Count - 1; processor.Emit(OpCodes.Ldarg_1); processor.Emit(OpCodes.Ldloca, isSetLocalIndex); processor.Emit(OpCodes.Call, boolMethodRef); - + // paramType param = null; processor.Emit(OpCodes.Ldnull); processor.Emit(OpCodes.Stloc, localIndex); - + // if(isSet) { jumpInstruction = processor.Create(OpCodes.Nop); processor.Emit(OpCodes.Ldloc, isSetLocalIndex); diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index e0bb496d2a..844acbb9ac 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -87,7 +87,7 @@ private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assem { fieldDefinition.IsPublic = true; } - + if (fieldDefinition.Name == nameof(NetworkManager.RpcReceive)) { fieldDefinition.IsPublic = true; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 42b6714b1b..742cbc1a4b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -3,7 +3,6 @@ using UnityEngine; using System.Reflection; using System.Linq; -using System.IO; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Messages; @@ -38,7 +37,7 @@ public enum __RpcExecStage #pragma warning restore 414 // restore assigned but its value is never used #pragma warning restore IDE1006 // restore naming rule violation check - + public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams sendParams, RpcDelivery delivery) { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; @@ -110,7 +109,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, RPCData = writer }; int messageSize; - + if (sendParams.Send.TargetClientIds != null) { messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIds, true); @@ -120,7 +119,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, // NativeArray doesn't implement required IReadOnlyList interface, but that's ok, pointer + length // will be more efficient anyway. messageSize = NetworkManager.SendMessage(message, networkDelivery, - (ulong*) sendParams.Send.TargetClientIdsNativeArray.Value.GetUnsafePtr(), + (ulong*)sendParams.Send.TargetClientIdsNativeArray.Value.GetUnsafePtr(), sendParams.Send.TargetClientIdsNativeArray.Value.Length); } else @@ -504,7 +503,7 @@ internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong client writer.WriteValueSafe((ushort)0x12AB); } } - + internal void SetNetworkVariableData(ref FastBufferReader reader) { if (NetworkVariableFields.Count == 0) @@ -519,7 +518,7 @@ internal void SetNetworkVariableData(ref FastBufferReader reader) if (varSize == 0) { reader.ReadValueSafe(out magic); - if (magic != (ushort) 0x12AB) + if (magic != (ushort)0x12AB) { NetworkLog.LogWarning($"Var data ended not on the magic value."); } @@ -551,7 +550,7 @@ internal void SetNetworkVariableData(ref FastBufferReader reader) } } reader.ReadValueSafe(out magic); - if (magic != (ushort) 0x12AB) + if (magic != (ushort)0x12AB) { NetworkLog.LogWarning($"Var data ended not on the magic value."); } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index b78f87a67a..fc92191b81 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using Unity.Collections; using Unity.Netcode.Messages; using UnityEngine; #if UNITY_EDITOR @@ -76,7 +75,7 @@ internal NetworkManagerHooks(NetworkManager manager) { m_NetworkManager = manager; } - + public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) { } @@ -116,8 +115,8 @@ public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelive public bool OnVerifyCanReceive(ulong senderId, Type messageType) { - if(m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) && - (client.ConnectionState == PendingClient.State.PendingApproval || + if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) && + (client.ConnectionState == PendingClient.State.PendingApproval || (client.ConnectionState == PendingClient.State.PendingConnection && messageType != typeof(ConnectionRequestMessage)))) { @@ -132,7 +131,7 @@ public bool OnVerifyCanReceive(ulong senderId, Type messageType) return true; } } - + private class NetworkManagerMessageSender : IMessageSender { private NetworkManager m_NetworkManager; @@ -141,10 +140,10 @@ public NetworkManagerMessageSender(NetworkManager manager) { m_NetworkManager = manager; } - + public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) { - + var length = batchData.Length; //TODO: Transport needs to have a way to send it data without copying and allocating here. var bytes = batchData.ToArray(); @@ -450,16 +449,16 @@ private void Initialize(bool server) this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); this.RegisterNetworkUpdate(NetworkUpdateStage.PostLateUpdate); - m_MessagingSystem = new MessagingSystem(new NetworkManagerMessageSender(this), this, UInt64.MaxValue); - + m_MessagingSystem = new MessagingSystem(new NetworkManagerMessageSender(this), this, ulong.MaxValue); + m_MessagingSystem.Hook(new NetworkManagerHooks(this)); #if DEVELOPMENT_BUILD || UNITY_EDITOR m_MessagingSystem.Hook(new ProfilingHooks()); #endif m_MessagingSystem.Hook(new MetricHooks(this)); - LocalClientId = UInt64.MaxValue; - + LocalClientId = ulong.MaxValue; + PendingClients.Clear(); ConnectedClients.Clear(); ConnectedClientsList.Clear(); @@ -1010,7 +1009,7 @@ public void Shutdown() NetworkTickSystem.Tick -= OnNetworkManagerTick; NetworkTickSystem = null; } - + if (m_MessagingSystem != null) { m_MessagingSystem.Dispose(); @@ -1251,9 +1250,9 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A } } - public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds, bool serverCanSendToServerId = false) - where T : INetworkMessage - where U : IReadOnlyList + public unsafe int SendMessage(in TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds, bool serverCanSendToServerId = false) + where TMessageType : INetworkMessage + where TClientIdListType : IReadOnlyList { // Prevent server sending to itself if (IsServer && !serverCanSendToServerId) @@ -1281,7 +1280,7 @@ public unsafe int SendMessage(in T message, NetworkDelivery delivery, in U public unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong* clientIds, int numClientIds, bool serverCanSendToServerId = false) - where T: INetworkMessage + where T : INetworkMessage { // Prevent server sending to itself if (IsServer && !serverCanSendToServerId) @@ -1304,12 +1303,12 @@ public unsafe int SendMessage(in T message, NetworkDelivery delivery, } return m_MessagingSystem.SendMessage(message, delivery, nonServerIds, newIdx); } - + return m_MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds); } public int SendMessage(in T message, NetworkDelivery delivery, ulong clientId, bool serverCanSendToServerId = false) - where T: INetworkMessage + where T : INetworkMessage { // Prevent server sending to itself if (IsServer && clientId == ServerClientId && !serverCanSendToServerId) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index b5f3726957..c4f112706b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1,13 +1,8 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Runtime.CompilerServices; -using NUnit.Framework; -using Unity.Collections; using Unity.Netcode.Messages; using UnityEngine; -using UnityEngine.Profiling.Memory.Experimental; namespace Unity.Netcode { @@ -897,7 +892,7 @@ public struct SceneObjectMetadata public ulong NetworkObjectId; public ulong OwnerClientId; public uint Hash; - + public bool IsPlayerObject; public bool HasParent; public bool IsSceneObject; @@ -907,27 +902,27 @@ public struct SceneObjectMetadata } public SceneObjectMetadata Metadata; - + #region If(Metadata.HasParent) - public ulong ParentObjectId; + public ulong ParentObjectId; #endregion - + #region If(Metadata.HasTransform) - public struct TransformData - { - public Vector3 Position; - public Quaternion Rotation; - } + public struct TransformData + { + public Vector3 Position; + public Quaternion Rotation; + } - public TransformData Transform; + public TransformData Transform; #endregion - + #region If(Metadata.IsReparented) - public bool IsLatestParentSet; - - #region If(IsLatestParentSet) - public ulong? LatestParent; - #endregion + public bool IsLatestParentSet; + + #region If(IsLatestParentSet) + public ulong? LatestParent; + #endregion #endregion #region If(data.HasNetworkVariables) @@ -948,7 +943,7 @@ public unsafe void Serialize(ref FastBufferWriter writer) { throw new OverflowException("Could not serialize SceneObject: Out of buffer space."); } - + writer.WriteValue(Metadata); if (Metadata.HasParent) @@ -1032,9 +1027,9 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) OwnerObject = this, TargetClientId = targetClientId }; - + NetworkObject parentNetworkObject = null; - + if (!AlwaysReplicateAsRoot && transform.parent != null) { parentNetworkObject = transform.parent.GetComponent(); @@ -1097,7 +1092,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, ref Fas //Attempt to create a local NetworkObject var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject( - sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.Hash, + sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.Hash, sceneObject.Metadata.OwnerClientId, parentNetworkId, position, rotation, sceneObject.Metadata.IsReparented); networkObject?.SetNetworkParenting(sceneObject.Metadata.IsReparented, sceneObject.LatestParent); @@ -1112,9 +1107,9 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, ref Fas // If we failed to load this NetworkObject, then skip past the network variable data variableData.ReadValueSafe(out ushort varSize); variableData.Seek(variableData.Position + varSize); - + variableData.ReadValueSafe(out ushort magic); - if (magic != (ushort) 0x12AB) + if (magic != (ushort)0x12AB) { NetworkLog.LogWarning($"Var data ended not on the magic value."); } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index f6859f6a5d..eb61183220 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -733,7 +733,7 @@ private void SendSnapshot(ulong clientId) // this sends the whole buffer // we'll need to build a per-client list SendMainBuffer = m_Snapshot.MainBuffer, - + Ack = new SnapshotDataMessage.AckData { LastReceivedSequence = m_ClientData[clientId].LastReceivedSequence, @@ -741,7 +741,7 @@ private void SendSnapshot(ulong clientId) } }; - + // write the snapshot: buffer, index, spawns, despawns WriteIndex(ref message); WriteSpawns(ref message, clientId); @@ -836,7 +836,7 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) } var sentSpawn = m_Snapshot.GetSpawnData(clientData, in m_Snapshot.Spawns[index], out var spawn); message.Spawns[j] = spawn; - + m_Snapshot.Spawns[index].TimesWritten++; clientData.SentSpawns.Add(sentSpawn); spawnWritten++; @@ -866,7 +866,7 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) { overSize = true; break; - } + } var sentDespawn = m_Snapshot.GetDespawnData(clientData, in m_Snapshot.Despawns[index], out var despawn); message.Despawns[j] = despawn; m_Snapshot.Despawns[index].TimesWritten++; diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs index 88764352d1..68afbe2651 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs @@ -1,7 +1,7 @@ -namespace Unity.Netcode +namespace Unity.Netcode { public struct BatchHeader { public ushort BatchSize; } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs index f2c550d114..f5bb5e1364 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Unity.Netcode { @@ -11,4 +11,4 @@ public Bind(Type boundType) BoundType = boundType; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index 5502ed440e..3101be718d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using Unity.Netcode.Messages; namespace Unity.Netcode diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs index 4a4edee35c..c265b29f1d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs @@ -1,7 +1,7 @@ -namespace Unity.Netcode +namespace Unity.Netcode { public interface IMessageSender { void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData); } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs index 6351f1ec3e..1464bec065 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/INetworkHooks.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Unity.Netcode { @@ -12,9 +12,9 @@ public interface INetworkHooks void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery); void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes); - - + + bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery); bool OnVerifyCanReceive(ulong senderId, Type messageType); } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs index 0946be0cb2..553aa29c54 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -1,10 +1,10 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode.Messages { - internal struct ChangeOwnershipMessage: INetworkMessage + internal struct ChangeOwnershipMessage : INetworkMessage { public ulong NetworkObjectId; public ulong OwnerClientId; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValueSafe(this); @@ -12,7 +12,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsClient) { return; @@ -50,4 +50,4 @@ public void Handle(ulong senderId, NetworkManager networkManager, int messageSiz networkManager.NetworkMetrics.TrackOwnershipChangeReceived(senderId, networkObject.NetworkObjectId, networkObject.name, messageSize); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs index a22733327e..2a10ec35f6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -1,16 +1,14 @@ -using System; +using System; using System.Collections.Generic; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; namespace Unity.Netcode.Messages { - internal struct ConnectionApprovedMessage: INetworkMessage + internal struct ConnectionApprovedMessage : INetworkMessage { public ulong OwnerClientId; public int NetworkTick; public int SceneObjectCount; - + // Not serialized, held as references to serialize NetworkVariable data public HashSet SpawnedObjectsList; @@ -24,8 +22,8 @@ public void Serialize(ref FastBufferWriter writer) writer.WriteValue(OwnerClientId); writer.WriteValue(NetworkTick); writer.WriteValue(SceneObjectCount); - - if(SceneObjectCount != 0) + + if (SceneObjectCount != 0) { // Serialize NetworkVariable data foreach (var sobj in SpawnedObjectsList) @@ -42,12 +40,12 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsClient) { return; } - + if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int))) { throw new OverflowException( @@ -91,4 +89,4 @@ public void Handle(ref FastBufferReader reader, ulong clientId, NetworkManager n } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs index 4972771c99..1e3a2f67a3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs @@ -1,18 +1,18 @@ -using System; +using System; using System.Runtime.InteropServices; namespace Unity.Netcode.Messages { - internal struct ConnectionRequestMessage: INetworkMessage + internal struct ConnectionRequestMessage : INetworkMessage { public ulong ConfigHash; - + [StructLayout(LayoutKind.Explicit, Size = 512)] - public struct ConnectionDataStorage: IFixedArrayStorage + public struct ConnectionDataStorage : IFixedArrayStorage { - + } - + public FixedUnmanagedArray ConnectionData; public bool ShouldSendConnectionData; @@ -21,7 +21,7 @@ public void Serialize(ref FastBufferWriter writer) { if (ShouldSendConnectionData) { - if(!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash) + + if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash) + FastBufferWriter.GetWriteSize(ConnectionData))) { throw new OverflowException( @@ -33,7 +33,7 @@ public void Serialize(ref FastBufferWriter writer) } else { - if(!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash))) + if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash))) { throw new OverflowException( $"Not enough space in the write buffer to serialize {nameof(ConnectionRequestMessage)}"); @@ -44,13 +44,13 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsServer) { return; } - - ConnectionRequestMessage message = new ConnectionRequestMessage(); + + var message = new ConnectionRequestMessage(); if (networkManager.NetworkConfig.ConnectionApproval) { if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash) + @@ -65,7 +65,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) return; } reader.ReadValue(out message.ConfigHash); - + if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) @@ -76,7 +76,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) networkManager.DisconnectClient(context.SenderId); return; } - + reader.ReadValue(out int length); if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize() * length)) { @@ -103,7 +103,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) return; } reader.ReadValue(out message.ConfigHash); - + if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) @@ -144,4 +144,4 @@ public void Handle(NetworkManager networkManager, ulong senderId) } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs index d946c85daf..83a7cae252 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -1,11 +1,9 @@ -using UnityEngine; - namespace Unity.Netcode.Messages { internal struct CreateObjectMessage : INetworkMessage { public NetworkObject.SceneObject ObjectInfo; - + public void Serialize(ref FastBufferWriter writer) { ObjectInfo.Serialize(ref writer); @@ -13,7 +11,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsClient) { return; @@ -29,4 +27,4 @@ public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager n networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject.NetworkObjectId, networkObject.name, reader.Length); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs index af51eb0c60..a6ecbb3175 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs @@ -1,9 +1,9 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode.Messages { internal struct DestroyObjectMessage : INetworkMessage { public ulong NetworkObjectId; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValueSafe(this); @@ -11,7 +11,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsClient) { return; @@ -38,4 +38,4 @@ public void Handle(ulong senderId, NetworkManager networkManager, int messageSiz networkManager.SpawnManager.OnDespawnObject(networkObject, true); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs index 4d35dc8197..d50ac193f3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs @@ -1,6 +1,6 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode.Messages { - internal struct NamedMessage: INetworkMessage + internal struct NamedMessage : INetworkMessage { public ulong Hash; public FastBufferWriter Data; @@ -19,4 +19,4 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) ((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(message.Hash, context.SenderId, ref reader); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs index c4ad3369d5..8bdc5521e0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Unity.Collections; @@ -14,11 +14,11 @@ internal struct NetworkVariableDeltaMessage : INetworkMessage { public ulong NetworkObjectId; public ushort NetworkBehaviourIndex; - + public HashSet DeliveryMappedNetworkVariableIndex; public ulong ClientId; public NetworkBehaviour NetworkBehaviour; - + public void Serialize(ref FastBufferWriter writer) { if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + @@ -36,7 +36,7 @@ public void Serialize(ref FastBufferWriter writer) // This var does not belong to the currently iterating delivery group. if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - writer.WriteValueSafe((short) 0); + writer.WriteValueSafe((short)0); } else { @@ -54,7 +54,7 @@ public void Serialize(ref FastBufferWriter writer) { if (!shouldWrite) { - writer.WriteValueSafe((ushort) 0); + writer.WriteValueSafe((ushort)0); } } else @@ -66,7 +66,7 @@ public void Serialize(ref FastBufferWriter writer) { if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - var tmpWriter = new FastBufferWriter(1300, Allocator.Temp, Int16.MaxValue); + var tmpWriter = new FastBufferWriter(1300, Allocator.Temp, short.MaxValue); NetworkBehaviour.NetworkVariableFields[k].WriteDelta(ref tmpWriter); writer.WriteValueSafe((ushort)tmpWriter.Length); @@ -96,7 +96,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.NetworkConfig.EnableNetworkVariable) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) @@ -119,7 +119,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) reader.ReadValue(out message.NetworkBehaviourIndex); message.Handle(context.SenderId, ref reader, networkManager); } - + public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager networkManager) { if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject)) @@ -234,4 +234,4 @@ public void Handle(ulong senderId, ref FastBufferReader reader, NetworkManager n } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs index e5e00982ba..05827cc273 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs @@ -1,19 +1,19 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode.Messages { public struct ParentSyncMessage : INetworkMessage { public ulong NetworkObjectId; - + public bool IsReparented; - + #region If(Metadata.IsReparented) - public bool IsLatestParentSet; - - #region If(IsLatestParentSet) - public ulong? LatestParent; - #endregion + public bool IsLatestParentSet; + + #region If(IsLatestParentSet) + public ulong? LatestParent; #endregion - + #endregion + public void Serialize(ref FastBufferWriter writer) { writer.WriteValueSafe(NetworkObjectId); @@ -30,7 +30,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsClient) { return; @@ -48,7 +48,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) message.LatestParent = latestParent; } } - + message.Handle(networkManager); } @@ -66,4 +66,4 @@ public void Handle(NetworkManager networkManager) } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs index 30abc04bc2..4246a6d66d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs @@ -1,5 +1,4 @@ -using System; -using System.Transactions; +using System; namespace Unity.Netcode.Messages { @@ -41,7 +40,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) throw new OverflowException("Not enough space in the buffer to read RPC data."); } reader.ReadValue(out message.Data); - message.Handle(ref reader, (NetworkManager) context.SystemOwner, context.SenderId); + message.Handle(ref reader, (NetworkManager)context.SystemOwner, context.SenderId); } public void Handle(ref FastBufferReader reader, NetworkManager networkManager, ulong senderId) @@ -99,4 +98,4 @@ public void Handle(ref FastBufferReader reader, NetworkManager networkManager, u } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs index c067ac19c4..2b2632f624 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs @@ -1,11 +1,11 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode.Messages { // Todo: Would be lovely to get this one nicely formatted with all the data it sends in the struct // like most of the other messages when we have some more time and can come back and refactor this. internal struct SceneEventMessage : INetworkMessage { public SceneEventData EventData; - + public void Serialize(ref FastBufferWriter writer) { EventData.Serialize(ref writer); @@ -16,4 +16,4 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) ((NetworkManager)context.SystemOwner).SceneManager.HandleSceneEvent(context.SenderId, ref reader); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs index 2c8f9aeefa..66a21cc497 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs @@ -1,5 +1,3 @@ -using System; - namespace Unity.Netcode.Messages { internal struct ServerLogMessage : INetworkMessage @@ -21,7 +19,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs) { var message = new ServerLogMessage(); @@ -33,7 +31,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) public void Handle(ulong senderId, NetworkManager networkManager, int messageSize) { - + networkManager.NetworkMetrics.TrackServerLogReceived(senderId, (uint)LogType, messageSize); switch (LogType) @@ -50,4 +48,4 @@ public void Handle(ulong senderId, NetworkManager networkManager, int messageSiz } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs index f04669c2cd..797e5e75db 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs @@ -1,16 +1,16 @@ -using Unity.Collections; +using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace Unity.Netcode.Messages { - internal struct SnapshotDataMessage: INetworkMessage + internal struct SnapshotDataMessage : INetworkMessage { public int CurrentTick; public ushort Sequence; - + public ushort Range; - + public byte[] SendMainBuffer; public NativeArray ReceiveMainBuffer; @@ -21,7 +21,7 @@ public struct AckData } public AckData Ack; - + public struct EntryData { public ulong NetworkObjectId; @@ -64,28 +64,28 @@ public unsafe void Serialize(ref FastBufferWriter writer) { writer.WriteValue(CurrentTick); writer.WriteValue(Sequence); - + writer.WriteValue(Range); writer.WriteBytes(SendMainBuffer, Range); writer.WriteValue(Ack); - + writer.WriteValue((ushort)Entries.Length); writer.WriteBytes((byte*)Entries.GetUnsafePtr(), Entries.Length * sizeof(EntryData)); - + writer.WriteValue((ushort)Spawns.Length); writer.WriteBytes((byte*)Spawns.GetUnsafePtr(), Spawns.Length * sizeof(SpawnData)); - + writer.WriteValue((ushort)Despawns.Length); writer.WriteBytes((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData)); } public static unsafe void Receive(ref FastBufferReader reader, NetworkContext context) { - NetworkManager networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; var message = new SnapshotDataMessage(); reader.ReadValue(out message.CurrentTick); reader.ReadValue(out message.Sequence); - + reader.ReadValue(out message.Range); message.ReceiveMainBuffer = new NativeArray(message.Range, Allocator.Temp); reader.ReadBytes((byte*)message.ReceiveMainBuffer.GetUnsafePtr(), message.Range); @@ -94,11 +94,11 @@ public static unsafe void Receive(ref FastBufferReader reader, NetworkContext co reader.ReadValue(out ushort length); message.Entries = new NativeArray(length, Allocator.Temp); reader.ReadBytes((byte*)message.Entries.GetUnsafePtr(), message.Entries.Length * sizeof(EntryData)); - + reader.ReadValue(out length); message.Spawns = new NativeArray(length, Allocator.Temp); reader.ReadBytes((byte*)message.Spawns.GetUnsafePtr(), message.Spawns.Length * sizeof(SpawnData)); - + reader.ReadValue(out length); message.Despawns = new NativeArray(length, Allocator.Temp); reader.ReadBytes((byte*)message.Despawns.GetUnsafePtr(), message.Despawns.Length * sizeof(DespawnData)); @@ -124,4 +124,4 @@ public void Handle(ulong senderId, NetworkManager networkManager) snapshotSystem.HandleSnapshot(senderId, this); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs index 0ff672e71d..380858391b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs @@ -1,11 +1,9 @@ -using AOT; - namespace Unity.Netcode.Messages { internal struct TimeSyncMessage : INetworkMessage { public int Tick; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValueSafe(this); @@ -13,7 +11,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - var networkManager = (NetworkManager) context.SystemOwner; + var networkManager = (NetworkManager)context.SystemOwner; if (!networkManager.IsClient) { return; @@ -28,4 +26,4 @@ public void Handle(ulong senderId, NetworkManager networkManager) networkManager.NetworkTimeSystem.Sync(time.Time, networkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(senderId) / 1000d); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs index d995b020b4..7fb49e5676 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs @@ -1,6 +1,6 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode.Messages { - internal struct UnnamedMessage: INetworkMessage + internal struct UnnamedMessage : INetworkMessage { public FastBufferWriter Data; @@ -14,4 +14,4 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) ((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeUnnamedMessage(context.SenderId, ref reader); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index be8b621a07..82bb11fee1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -1,12 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using Unity.Collections; -using Unity.Netcode.Transports.UNET; using UnityEngine; -using UnityEngine.UIElements; namespace Unity.Netcode { @@ -16,10 +13,10 @@ public class InvalidMessageStructureException : SystemException public InvalidMessageStructureException() { } public InvalidMessageStructureException(string issue) : base(issue) { } } - - public class MessagingSystem: IDisposable + + public class MessagingSystem : IDisposable { -#region Internal Types + #region Internal Types private struct ReceiveQueueItem { public FastBufferReader Reader; @@ -41,16 +38,16 @@ public SendQueueItem(NetworkDelivery delivery, int writerSize, Allocator writerA BatchHeader = default; } } - + internal delegate void MessageHandler(ref FastBufferReader reader, NetworkContext context); -#endregion + #endregion -#region Private Members + #region Private Members private DynamicUnmanagedArray m_IncomingMessageQueue = new DynamicUnmanagedArray(16); private MessageHandler[] m_MessageHandlers = new MessageHandler[255]; private Type[] m_ReverseTypeMap = new Type[255]; - + private Dictionary m_MessageTypes = new Dictionary(); private NativeHashMap>> m_SendQueues = new NativeHashMap>>(64, Allocator.Persistent); @@ -61,7 +58,7 @@ public SendQueueItem(NetworkDelivery delivery, int writerSize, Allocator writerA private IMessageSender m_MessageSender; private ulong m_LocalClientId; private bool m_Disposed; -#endregion + #endregion internal Type[] MessageTypes => m_ReverseTypeMap; internal MessageHandler[] MessageHandlers => m_MessageHandlers; @@ -72,14 +69,14 @@ internal byte GetMessageType(Type t) return m_MessageTypes[t]; } - public MessagingSystem(IMessageSender messageSender, object owner, ulong localClientId = Int64.MaxValue) + public MessagingSystem(IMessageSender messageSender, object owner, ulong localClientId = long.MaxValue) { try { m_LocalClientId = localClientId; m_MessageSender = messageSender; m_Owner = owner; - + var interfaceType = typeof(INetworkMessage); var implementationTypes = new List(); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) @@ -105,7 +102,7 @@ public MessagingSystem(IMessageSender messageSender, object owner, ulong localCl var allowedToBind = attributes.Length == 0 && m_Owner is NetworkManager; for (var i = 0; i < attributes.Length; ++i) { - Bind bindAttribute = (Bind) attributes[i]; + var bindAttribute = (Bind)attributes[i]; if ( (bindAttribute.BoundType != null && bindAttribute.BoundType.IsInstanceOfType(m_Owner)) || @@ -126,13 +123,13 @@ public MessagingSystem(IMessageSender messageSender, object owner, ulong localCl } } - implementationTypes.Sort((a, b) => String.CompareOrdinal(a.FullName, b.FullName)); - foreach(var type in implementationTypes) + implementationTypes.Sort((a, b) => string.CompareOrdinal(a.FullName, b.FullName)); + foreach (var type in implementationTypes) { RegisterMessageType(type); } } - catch(Exception) + catch (Exception) { Dispose(); throw; @@ -147,7 +144,7 @@ public void Dispose() } var keys = m_SendQueues.GetKeyArray(Allocator.Temp); - using(keys) + using (keys) { foreach (var key in keys) { @@ -173,14 +170,14 @@ public void Hook(INetworkHooks hooks) { m_Hooks.Add(hooks); } - + private void RegisterMessageType(Type messageType) { if (!typeof(INetworkMessage).IsAssignableFrom(messageType)) { throw new ArgumentException("RegisterMessageType types must be INetworkMessage types."); } - + var method = messageType.GetMethod("Receive"); if (method == null) { @@ -195,18 +192,18 @@ private void RegisterMessageType(Type messageType) $"{messageType.Name}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); } - m_MessageHandlers[m_HighMessageType] = (MessageHandler) asDelegate; + m_MessageHandlers[m_HighMessageType] = (MessageHandler)asDelegate; m_ReverseTypeMap[m_HighMessageType] = messageType; m_MessageTypes[messageType] = m_HighMessageType++; } - + internal void HandleIncomingData(ulong clientId, ArraySegment data, float receiveTime) { unsafe { fixed (byte* nativeData = data.Array) { - FastBufferReader batchReader = + var batchReader = new FastBufferReader(nativeData, Allocator.None, data.Count, data.Offset); if (!batchReader.TryBeginRead(sizeof(BatchHeader))) { @@ -282,7 +279,7 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, reader.Dispose(); return; } - + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length); @@ -354,9 +351,9 @@ private bool CanSend(ulong clientId, Type messageType, NetworkDelivery delivery) return true; } - internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in U clientIds) - where T: INetworkMessage - where U: IReadOnlyList + internal unsafe int SendMessage(in TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds) + where TMessageType : INetworkMessage + where TClientIdListType : IReadOnlyList { var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? 64000 : 1300; var tmpSerializer = new FastBufferWriter(1300, Allocator.Temp, maxSize); @@ -369,14 +366,14 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in { var clientId = clientIds[i]; - if (!CanSend(clientId, typeof(T), delivery)) + if (!CanSend(clientId, typeof(TMessageType), delivery)) { continue; } - + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(T), delivery); + m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(TMessageType), delivery); } ref var sendQueueItem = ref m_SendQueues[clientId].Value; @@ -400,10 +397,10 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in ref var writeQueueItem = ref sendQueueItem.GetValueRef(sendQueueItem.Count - 1); writeQueueItem.Writer.TryBeginWrite(sizeof(MessageHeader) + tmpSerializer.Length); - MessageHeader header = new MessageHeader + var header = new MessageHeader { - MessageSize = (short) tmpSerializer.Length, - MessageType = m_MessageTypes[typeof(T)], + MessageSize = (short)tmpSerializer.Length, + MessageType = m_MessageTypes[typeof(TMessageType)], }; @@ -424,7 +421,7 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in writeQueueItem.BatchHeader.BatchSize++; for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { - m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(T), delivery, tmpSerializer.Length + sizeof(MessageHeader)); + m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(TMessageType), delivery, tmpSerializer.Length + sizeof(MessageHeader)); } } #pragma warning restore CS0728 // Warns that tmpSerializer may be reassigned within Serialize, but Serialize does not reassign it. @@ -432,7 +429,7 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in return tmpSerializer.Length; } } - + private struct PointerListWrapper : IReadOnlyList where T : unmanaged { @@ -470,15 +467,15 @@ IEnumerator IEnumerable.GetEnumerator() internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong* clientIds, int numClientIds) - where T: INetworkMessage + where T : INetworkMessage { return SendMessage(message, delivery, new PointerListWrapper(clientIds, numClientIds)); } internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong clientId) - where T: INetworkMessage + where T : INetworkMessage { - ulong* clientIds = stackalloc ulong[] {clientId}; + ulong* clientIds = stackalloc ulong[] { clientId }; return SendMessage(message, delivery, new PointerListWrapper(clientIds, 1)); } @@ -496,22 +493,22 @@ internal void ProcessSendQueues() queueItem.Writer.Dispose(); continue; } - + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { m_Hooks[hookIdx].OnBeforeSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); } - + queueItem.Writer.Seek(0); #if UNITY_EDITOR || DEVELOPMENT_BUILD // Skipping the Verify and sneaking the write mark in because we know it's fine. queueItem.Writer.AllowedWriteMark = 2; #endif queueItem.Writer.WriteValue(queueItem.BatchHeader); - + m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); queueItem.Writer.Dispose(); - + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { m_Hooks[hookIdx].OnAfterSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs index 1e43d6be92..f973177f2a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkContext.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode +namespace Unity.Netcode { public ref struct NetworkContext { @@ -7,4 +7,4 @@ public ref struct NetworkContext public float Timestamp; public MessageHeader Header; } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs index 0f473f436d..741aac5697 100644 --- a/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs +++ b/com.unity.netcode.gameobjects/Runtime/Metrics/MetricHooks.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.IO; -using Unity.Profiling; namespace Unity.Netcode { @@ -14,7 +11,7 @@ public MetricHooks(NetworkManager networkManager) m_NetworkManager = networkManager; } - + public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) { } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs index ac3933226b..12298ae68d 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.IO; namespace Unity.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index 90d1ca58d0..4efa4e92e4 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.IO; namespace Unity.Netcode { @@ -459,7 +458,7 @@ public struct NetworkListEvent /// /// Enum representing the different operations available for triggering an event. /// - public enum EventType: byte + public enum EventType : byte { /// /// Add diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs index 85c280ae91..88eeaaccb4 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs @@ -1,7 +1,6 @@ #if !NET35 using System.Collections; using System.Collections.Generic; -using System.IO; namespace Unity.Netcode { @@ -90,19 +89,19 @@ public override void WriteDelta(ref FastBufferWriter writer) switch (m_DirtyEvents[i].Type) { case NetworkSetEvent.EventType.Add: - { - writer.WriteValueSafe(m_DirtyEvents[i].Value); - } + { + writer.WriteValueSafe(m_DirtyEvents[i].Value); + } break; case NetworkSetEvent.EventType.Remove: - { - writer.WriteValueSafe(m_DirtyEvents[i].Value); - } + { + writer.WriteValueSafe(m_DirtyEvents[i].Value); + } break; case NetworkSetEvent.EventType.Clear: - { - //Nothing has to be written - } + { + //Nothing has to be written + } break; } } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs index c10394a81b..d2302f6749 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using UnityEngine; -using System.IO; using System; namespace Unity.Netcode @@ -114,7 +113,7 @@ public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { T previousValue = m_InternalValue; reader.ReadValueSafe(out m_InternalValue); - + if (keepDirtyDelta) { m_IsDirty = true; diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index 055d67e2d1..f0b2345132 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace Unity.Netcode { /// diff --git a/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs b/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs index 5637b3d913..a328be4926 100644 --- a/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs +++ b/com.unity.netcode.gameobjects/Runtime/Profiling/ProfilingHooks.cs @@ -1,43 +1,42 @@ using System; using System.Collections.Generic; -using System.IO; using Unity.Profiling; namespace Unity.Netcode { internal class ProfilingHooks : INetworkHooks { - private Dictionary m_handlerProfilerMarkers = new Dictionary(); - private Dictionary m_senderProfilerMarkers = new Dictionary(); + private Dictionary m_HandlerProfilerMarkers = new Dictionary(); + private Dictionary m_SenderProfilerMarkers = new Dictionary(); private readonly ProfilerMarker m_SendBatch = new ProfilerMarker($"{nameof(MessagingSystem)}.SendBatch"); private readonly ProfilerMarker m_ReceiveBatch = new ProfilerMarker($"{nameof(MessagingSystem)}.ReceiveBatchBatch"); private ProfilerMarker GetHandlerProfilerMarker(Type type) { - var result = m_handlerProfilerMarkers.TryGetValue(type, out var marker); + var result = m_HandlerProfilerMarkers.TryGetValue(type, out var marker); if (result) { return marker; } marker = new ProfilerMarker($"{nameof(MessagingSystem)}.DeserializeAndHandle.{type.Name}"); - m_handlerProfilerMarkers[type] = marker; + m_HandlerProfilerMarkers[type] = marker; return marker; } private ProfilerMarker GetSenderProfilerMarker(Type type) { - var result = m_senderProfilerMarkers.TryGetValue(type, out var marker); + var result = m_SenderProfilerMarkers.TryGetValue(type, out var marker); if (result) { return marker; } marker = new ProfilerMarker($"{nameof(MessagingSystem)}.SerializeAndEnqueue.{type.Name}"); - m_senderProfilerMarkers[type] = marker; + m_SenderProfilerMarkers[type] = marker; return marker; } - + public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery) { GetSenderProfilerMarker(messageType).Begin(); diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 39c1779816..83add8268f 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System; -using System.IO; using System.Linq; using Unity.Netcode.Messages; using UnityEngine; @@ -572,13 +571,13 @@ private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading /// private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress) { - + ClientSynchEventData.SceneEventGuid = sceneEventProgress.Guid; ClientSynchEventData.SceneIndex = sceneEventProgress.SceneBuildIndex; ClientSynchEventData.SceneEventType = sceneEventProgress.SceneEventType; ClientSynchEventData.ClientsCompleted = sceneEventProgress.DoneClients; ClientSynchEventData.ClientsTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(); - + var message = new SceneEventMessage { EventData = ClientSynchEventData @@ -1146,9 +1145,9 @@ internal void SynchronizeNetworkObjects(ulong clientId) foreach (var sceneIndex in ClientSynchEventData.ScenesToSynchronize) { m_NetworkManager.NetworkMetrics.TrackSceneEventSent( - clientId, (uint) ClientSynchEventData.SceneEventType, ScenesInBuild[(int) sceneIndex], size); + clientId, (uint)ClientSynchEventData.SceneEventType, ScenesInBuild[(int)sceneIndex], size); } - + // Notify the local server that the client has been sent the SceneEventData.SceneEventTypes.S2C_Event_Sync event OnSceneEvent?.Invoke(new SceneEvent() { @@ -1281,8 +1280,8 @@ private void ClientLoadedSynchronization(uint sceneIndex, int sceneHandle) ClientSynchEventData.LoadSceneMode = loadSceneMode; ClientSynchEventData.SceneEventType = SceneEventData.SceneEventTypes.C2S_LoadComplete; ClientSynchEventData.SceneIndex = sceneIndex; - - + + var message = new SceneEventMessage { EventData = ClientSynchEventData diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 2ad988915f..2b6905b79f 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -2,8 +2,6 @@ using System; using System.Linq; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using UnityEngine; using UnityEngine.SceneManagement; @@ -380,7 +378,7 @@ internal void WriteSceneSynchronizationData(ref FastBufferWriter writer) // Write the number of NetworkObjects we are serializing writer.WriteValueSafe(m_NetworkObjectsSync.Count()); - for(var i = 0; i < m_NetworkObjectsSync.Count(); ++i) + for (var i = 0; i < m_NetworkObjectsSync.Count(); ++i) { var noStart = writer.Position; var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId); @@ -515,7 +513,7 @@ internal void CopySceneSyncrhonizationData(ref FastBufferReader reader) { throw new OverflowException("Not enough space in the buffer to read recorded synchronization data size."); } - + m_HasInternalBuffer = true; InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, sizeToCopy); } @@ -532,7 +530,7 @@ internal void DeserializeScenePlacedObjects() { // is not packed! InternalBuffer.ReadValueSafe(out ushort newObjectsCount); - + for (ushort i = 0; i < newObjectsCount; i++) { InternalBuffer.ReadValueSafe(out int sceneHandle); @@ -540,7 +538,7 @@ internal void DeserializeScenePlacedObjects() m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneHandle); // Deserialize the NetworkObject - NetworkObject.SceneObject sceneObject = new NetworkObject.SceneObject(); + var sceneObject = new NetworkObject.SceneObject(); sceneObject.Deserialize(ref InternalBuffer); NetworkObject.AddSceneObject(sceneObject, ref InternalBuffer, m_NetworkManager); } @@ -692,9 +690,9 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) InternalBuffer.ReadValueSafe(out int handle); m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(handle); - NetworkObject.SceneObject sceneObject = new NetworkObject.SceneObject(); + var sceneObject = new NetworkObject.SceneObject(); sceneObject.Deserialize(ref InternalBuffer); - + var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, ref InternalBuffer, networkManager); if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 92c974d1e6..2170a6c99c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -48,7 +48,7 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in LengthInternal = Math.Max(1, length == -1 ? buffer.Length : length); if (allocator == Allocator.None) { - BufferPointer = (byte*) buffer.GetUnsafePtr() + offset; + BufferPointer = (byte*)buffer.GetUnsafePtr() + offset; } else { @@ -88,7 +88,7 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - BufferPointer = (byte*) bufferPtr; + BufferPointer = (byte*)bufferPtr; } PositionInternal = 0; @@ -123,7 +123,7 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - BufferPointer = (byte*) bufferPtr; + BufferPointer = (byte*)bufferPtr; } PositionInternal = 0; @@ -148,13 +148,13 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in LengthInternal = Math.Max(1, length); if (allocator == Allocator.None) { - BufferPointer = buffer + offset; + BufferPointer = buffer + offset; } else { void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); - BufferPointer = (byte*) bufferPtr; + BufferPointer = (byte*)bufferPtr; } PositionInternal = 0; @@ -401,7 +401,7 @@ public unsafe byte[] ToArray() { ReadValueSafe(out int size); value = new T[size]; - for(var i = 0; i < size; ++i) + for (var i = 0; i < size; ++i) { ReadNetworkSerializable(out value[i]); } @@ -709,7 +709,7 @@ public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) /// The value to copy /// Any unmanaged type [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValue(out FixedUnmanagedArray value, int count) + public unsafe void ReadValue(out FixedUnmanagedArray value, int count) where TPropertyType : unmanaged where TStorageType : unmanaged, IFixedArrayStorage { @@ -741,7 +741,7 @@ public unsafe void ReadValue(out FixedUnmanagedArra /// The value to copy /// Any unmanaged type [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValueSafe(out FixedUnmanagedArray value, int count) + public unsafe void ReadValueSafe(out FixedUnmanagedArray value, int count) where TPropertyType : unmanaged where TStorageType : unmanaged, IFixedArrayStorage { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 8ba50099a9..19b8b812a9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -175,7 +175,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out GameObjec value = null; } - + /// /// Read an array of GameObjects /// @@ -236,7 +236,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkOb value = null; } - + /// /// Read an array of NetworkObjects /// @@ -299,7 +299,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBe value = null; } - + /// /// Read an array of NetworkBehaviours /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 5f21ca6cda..32686811b0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -76,7 +76,7 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, size); #endif - BufferPointer = (byte*) buffer; + BufferPointer = (byte*)buffer; PositionInternal = 0; m_Length = 0; CapacityInternal = size; @@ -176,7 +176,7 @@ internal unsafe void Grow(int additionalSizeRequired) #endif UnsafeUtility.MemCpy(buffer, BufferPointer, Length); UnsafeUtility.Free(BufferPointer, m_Allocator); - BufferPointer = (byte*) buffer; + BufferPointer = (byte*)buffer; CapacityInternal = newSize; } @@ -370,7 +370,7 @@ public static int GetWriteSize(string s, bool oneByteChars = false) /// /// The value to write /// - public void WriteNetworkSerializable(in T value) where T: INetworkSerializable + public void WriteNetworkSerializable(in T value) where T : INetworkSerializable { var bufferSerializer = new BufferSerializer(new BufferSerializerWriter(ref this)); value.NetworkSerialize(bufferSerializer); @@ -381,7 +381,7 @@ public void WriteNetworkSerializable(in T value) where T: INetworkSerializabl /// /// The value to write /// - public void WriteNetworkSerializable(INetworkSerializable[] array, int count = -1, int offset = 0) where T: INetworkSerializable + public void WriteNetworkSerializable(INetworkSerializable[] array, int count = -1, int offset = 0) where T : INetworkSerializable { int sizeInTs = count != -1 ? count : array.Length - offset; WriteValueSafe(sizeInTs); @@ -714,8 +714,8 @@ public unsafe void CopyFrom(ref FastBufferWriter other) { WriteBytes(other.BufferPointer, other.PositionInternal); } - - + + /// /// Get the size required to write a FixedUnmanagedArray /// @@ -723,7 +723,7 @@ public unsafe void CopyFrom(ref FastBufferWriter other) /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(in FixedUnmanagedArray value, int count) + public static unsafe int GetWriteSize(in FixedUnmanagedArray value, int count) where TPropertyType : unmanaged where TStorageType : unmanaged, IFixedArrayStorage { @@ -785,7 +785,7 @@ public unsafe void WriteValueSafe(in FixedUnmanaged { throw new OverflowException("Writing past the end of the buffer"); } - + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)value.GetArrayPtr(), len); PositionInternal += len; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs index ac028dc14c..54b979b729 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs @@ -2,6 +2,6 @@ namespace Unity.Netcode { public interface INetworkSerializable { - void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation; + void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index eb77881669..47ef1f35ce 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using Unity.Collections; using Unity.Netcode.Messages; using UnityEngine; @@ -144,7 +142,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId) networkObject.OwnerClientId = clientId; - + var message = new ChangeOwnershipMessage { NetworkObjectId = networkObject.NetworkObjectId, @@ -285,7 +283,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo { throw new SpawnStateException("Object is already spawned"); } - + SpawnNetworkObjectLocallyCommon(networkObject, networkId, sceneObject, playerObject, ownerClientId, destroyWithScene); } @@ -307,7 +305,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkO { networkObject.SetNetworkVariableData(ref variableData); } - + SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Metadata.NetworkObjectId, sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.IsPlayerObject, sceneObject.Metadata.OwnerClientId, destroyWithScene); } diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs index 38d093caf4..f451bd2fef 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using UnityEngine; namespace Unity.Netcode { @@ -48,7 +47,7 @@ private unsafe void Resize() { m_Capacity *= 2; var data = (T*)UnsafeUtility.Malloc(m_Capacity * sizeof(T), UnsafeUtility.AlignOf(), m_Allocator); - UnsafeUtility.MemCpy(data, m_Data, m_Length*sizeof(T)); + UnsafeUtility.MemCpy(data, m_Data, m_Length * sizeof(T)); UnsafeUtility.Free(m_Data, m_Allocator); m_Data = data; } @@ -74,7 +73,7 @@ public void Clear() public IEnumerator GetEnumerator() { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() @@ -84,11 +83,11 @@ IEnumerator IEnumerable.GetEnumerator() public static unsafe Ref> CreateRef(int capacity = 16) { - DynamicUnmanagedArray* array = - (DynamicUnmanagedArray*) UnsafeUtility.Malloc( + var array = + (DynamicUnmanagedArray*)UnsafeUtility.Malloc( sizeof(DynamicUnmanagedArray), UnsafeUtility.AlignOf>(), Allocator.Persistent); - + array->m_Data = (T*)UnsafeUtility.Malloc(capacity * sizeof(T), UnsafeUtility.AlignOf(), Allocator.Persistent); array->m_Allocator = Allocator.Persistent; array->m_Length = 0; diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs index b0c138a883..04cac54419 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Netcode @@ -23,10 +21,10 @@ namespace Unity.Netcode /// public interface IFixedArrayStorage { - + } - public struct FixedUnmanagedArray : IReadOnlyList + public struct FixedUnmanagedArray : IReadOnlyList where TPropertyType : unmanaged where TStorageType : unmanaged, IFixedArrayStorage { @@ -36,16 +34,16 @@ public struct FixedUnmanagedArray : IReadOnlyList sizeof(TStorageType)/sizeof(TPropertyType); + public unsafe int Capacity => sizeof(TStorageType) / sizeof(TPropertyType); public bool IsReadOnly => false; public unsafe TPropertyType[] ToArray() { - TPropertyType[] ret = new TPropertyType[Count]; + var ret = new TPropertyType[Count]; fixed (TPropertyType* b = ret) { fixed (TStorageType* ptr = &m_Data) @@ -55,14 +53,14 @@ public unsafe TPropertyType[] ToArray() } return ret; } - + public unsafe FixedUnmanagedArray(TPropertyType* seedData, int size) { if (size > sizeof(TStorageType)) { throw new OverflowException("Seed data was larger than provided storage class."); } - + m_Data = new TStorageType(); fixed (TStorageType* ptr = &m_Data) { @@ -99,7 +97,7 @@ public unsafe FixedUnmanagedArray(TPropertyType[] seedData, int size) { throw new ArgumentException("Size cannot be greater than seed data's length."); } - + m_Data = new TStorageType(); fixed (TStorageType* ptr = &m_Data) fixed (TPropertyType* seedPtr = seedData) @@ -114,7 +112,7 @@ public unsafe FixedUnmanagedArray(TPropertyType[] seedData, int size) { fixed (TStorageType* ptr = &m_Data) { - return (TPropertyType*) ptr; + return (TPropertyType*)ptr; } } @@ -125,7 +123,7 @@ public unsafe TPropertyType this[int index] { fixed (TStorageType* ptr = &m_Data) { - TPropertyType* reinterpretPtr = (TPropertyType*) ptr; + var reinterpretPtr = (TPropertyType*)ptr; return reinterpretPtr[index]; } } @@ -134,7 +132,7 @@ public unsafe TPropertyType this[int index] { fixed (TStorageType* ptr = &m_Data) { - TPropertyType* reinterpretPtr = (TPropertyType*) ptr; + var reinterpretPtr = (TPropertyType*)ptr; reinterpretPtr[index] = value; } } @@ -145,7 +143,7 @@ public unsafe ref TPropertyType GetValueRef(int index) { fixed (TStorageType* ptr = &m_Data) { - TPropertyType* reinterpretPtr = (TPropertyType*) ptr; + var reinterpretPtr = (TPropertyType*)ptr; return ref reinterpretPtr[index]; } } @@ -172,7 +170,7 @@ public void Clear() public IEnumerator GetEnumerator() { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs deleted file mode 100644 index d039ea6027..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NUnit.Framework; - -namespace Unity.Netcode.EditorTests -{ - public class MessageBatcherTests - { - [Test] - public void SendWithThreshold() - { - } - - [Test] - public void SendWithoutThreshold() - { - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs.meta deleted file mode 100644 index 067c563c08..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 696f48f7bbcaf9f40b72311d4b6da74c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs index b5ce9d446c..9794ca415e 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; @@ -10,7 +10,7 @@ namespace Unity.Netcode.EditorTests public class MessageReceivingTests { [Bind(typeof(MessageReceivingTests))] - struct TestMessage : INetworkMessage + private struct TestMessage : INetworkMessage { public int A; public int B; @@ -32,13 +32,13 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) } private MessagingSystem m_MessagingSystem; - + [SetUp] public void SetUp() { TestMessage.Deserialized = false; TestMessage.DeserializedValues.Clear(); - + m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this); } @@ -61,10 +61,10 @@ private TestMessage GetMessage() [Test] public void WhenHandlingAMessage_ReceiveMethodIsCalled() - { + { var messageHeader = new MessageHeader { - MessageSize = (short) UnsafeUtility.SizeOf(), + MessageSize = (short)UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), }; var message = GetMessage(); @@ -74,7 +74,7 @@ public void WhenHandlingAMessage_ReceiveMethodIsCalled() { writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message)); writer.WriteValue(message); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { @@ -95,7 +95,7 @@ public void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingM }; var messageHeader = new MessageHeader { - MessageSize = (short) UnsafeUtility.SizeOf(), + MessageSize = (short)UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), }; var message = GetMessage(); @@ -103,19 +103,19 @@ public void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingM var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { - writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + - FastBufferWriter.GetWriteSize(messageHeader) + + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) + FastBufferWriter.GetWriteSize(message)); writer.WriteValue(batchHeader); writer.WriteValue(messageHeader); writer.WriteValue(message); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); Assert.IsFalse(TestMessage.Deserialized); - Assert.IsEmpty(TestMessage.DeserializedValues);; + Assert.IsEmpty(TestMessage.DeserializedValues); ; } } } @@ -129,7 +129,7 @@ public void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled }; var messageHeader = new MessageHeader { - MessageSize = (short) UnsafeUtility.SizeOf(), + MessageSize = (short)UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), }; var message = GetMessage(); @@ -137,13 +137,13 @@ public void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { - writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + - FastBufferWriter.GetWriteSize(messageHeader) + + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) + FastBufferWriter.GetWriteSize(message)); writer.WriteValue(batchHeader); writer.WriteValue(messageHeader); writer.WriteValue(message); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { @@ -165,7 +165,7 @@ public void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethod }; var messageHeader = new MessageHeader { - MessageSize = (short) UnsafeUtility.SizeOf(), + MessageSize = (short)UnsafeUtility.SizeOf(), MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)), }; var message = GetMessage(); @@ -174,22 +174,22 @@ public void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethod var writer = new FastBufferWriter(1300, Allocator.Temp); using (writer) { - writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + - FastBufferWriter.GetWriteSize(messageHeader) * 2 + + writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) + + FastBufferWriter.GetWriteSize(messageHeader) * 2 + FastBufferWriter.GetWriteSize(message) * 2); writer.WriteValue(batchHeader); writer.WriteValue(messageHeader); writer.WriteValue(message); writer.WriteValue(messageHeader); writer.WriteValue(message2); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { m_MessagingSystem.HandleIncomingData(0, new ArraySegment(writer.ToArray()), 0); Assert.IsFalse(TestMessage.Deserialized); Assert.IsEmpty(TestMessage.DeserializedValues); - + m_MessagingSystem.ProcessIncomingMessageQueue(); Assert.IsTrue(TestMessage.Deserialized); Assert.AreEqual(2, TestMessage.DeserializedValues.Count); @@ -199,4 +199,4 @@ public void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethod } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs index 79d084fd2e..c8b37b6598 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -1,28 +1,27 @@ - + using NUnit.Framework; -using Unity.Netcode.Transports.UNET; namespace Unity.Netcode.EditorTests { public class MessageRegistrationTests { - class MessagingSystemOwnerOne + private class MessagingSystemOwnerOne { - + } - class MessagingSystemOwnerTwo + private class MessagingSystemOwnerTwo { - + } - + [Bind(typeof(MessagingSystemOwnerOne))] - struct TestMessageOne : INetworkMessage + private struct TestMessageOne : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -30,17 +29,17 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - + } } - + [Bind(typeof(MessagingSystemOwnerOne))] - struct TestMessageTwo : INetworkMessage + private struct TestMessageTwo : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -48,17 +47,17 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - + } } - + [Bind(typeof(MessagingSystemOwnerTwo))] - struct TestMessageThree : INetworkMessage + private struct TestMessageThree : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -66,17 +65,17 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - + } } - + [Bind(null)] - struct TestMessageFour : INetworkMessage + private struct TestMessageFour : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -84,7 +83,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader, NetworkContext context) { - + } } @@ -135,7 +134,7 @@ public void WhenCreatingMessageSystem_BoundTypeMessageHandlersAreRegistered() MessagingSystem.MessageHandler handlerFour = TestMessageFour.Receive; var foundHandlerOne = systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]; - + Assert.AreEqual(handlerOne, systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]); Assert.AreEqual(handlerTwo, @@ -148,18 +147,18 @@ public void WhenCreatingMessageSystem_BoundTypeMessageHandlersAreRegistered() } #region WhenCreatingMessageSystem_MissingReceiveHandlerThrowsException - class BrokenSystemOwnerOne + private class BrokenSystemOwnerOne { - + } - + [Bind(typeof(BrokenSystemOwnerOne))] - struct TestMessageFive : INetworkMessage + private struct TestMessageFive : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -176,18 +175,18 @@ public void WhenCreatingMessageSystem_MissingReceiveHandlerThrowsException() #endregion #region WhenCreatingMessageSystem_ReceiveHandlerWithIncorrectParametersThrowsException - class BrokenSystemOwnerTwo + private class BrokenSystemOwnerTwo { - + } - + [Bind(typeof(BrokenSystemOwnerTwo))] - struct TestMessageSix : INetworkMessage + private struct TestMessageSix : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -195,7 +194,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(ref FastBufferReader reader) { - + } } @@ -209,18 +208,18 @@ public void WhenCreatingMessageSystem_ReceiveHandlerWithIncorrectParametersThrow #endregion #region WhenCreatingMessageSystem_ReceiveHandlerWithMissingRefSpecifierThrowsException - class BrokenSystemOwnerThree + private class BrokenSystemOwnerThree { - + } - + [Bind(typeof(BrokenSystemOwnerThree))] - struct TestMessageSeven : INetworkMessage + private struct TestMessageSeven : INetworkMessage { public int A; public int B; public int C; - + public void Serialize(ref FastBufferWriter writer) { writer.WriteValue(this); @@ -228,7 +227,7 @@ public void Serialize(ref FastBufferWriter writer) public static void Receive(FastBufferReader reader, NetworkContext context) { - + } } @@ -241,4 +240,4 @@ public void WhenCreatingMessageSystem_ReceiveHandlerWithMissingRefSpecifierThrow } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs index ac7e1e3781..419b2ba6b2 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; @@ -9,7 +9,7 @@ namespace Unity.Netcode.EditorTests public class MessageSendingTests { [Bind(typeof(MessageSendingTests))] - struct TestMessage : INetworkMessage + private struct TestMessage : INetworkMessage { public int A; public int B; @@ -27,7 +27,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) } } - class TestMessageSender : IMessageSender + private class TestMessageSender : IMessageSender { public List MessageQueue = new List(); @@ -39,13 +39,13 @@ public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter private TestMessageSender m_MessageSender; private MessagingSystem m_MessagingSystem; - private ulong[] m_Clients = {0}; - + private ulong[] m_Clients = { 0 }; + [SetUp] public void SetUp() { TestMessage.Serialized = false; - + m_MessageSender = new TestMessageSender(); m_MessagingSystem = new MessagingSystem(m_MessageSender, this); m_MessagingSystem.ClientConnected(0); @@ -89,7 +89,7 @@ public void WhenProcessingSendQueue_MessageIsSent() { var message = GetMessage(); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); } @@ -101,7 +101,7 @@ public void WhenSendingMultipleMessages_MessagesAreBatched() m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); } @@ -115,7 +115,7 @@ public void WhenNotExceedingBatchSize_NewBatchesAreNotCreated() { m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); } - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); } @@ -129,7 +129,7 @@ public void WhenExceedingBatchSize_NewBatchesAreCreated() { m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); } - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(2, m_MessageSender.MessageQueue.Count); } @@ -143,7 +143,7 @@ public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated() { m_MessagingSystem.SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, m_Clients); } - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); } @@ -155,7 +155,7 @@ public void WhenSwitchingDelivery_NewBatchesAreCreated() m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message, NetworkDelivery.Unreliable, m_Clients); - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(2, m_MessageSender.MessageQueue.Count); } @@ -167,7 +167,7 @@ public void WhenSwitchingChannel_NewBatchesAreNotCreated() m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); - + m_MessagingSystem.ProcessSendQueues(); Assert.AreEqual(1, m_MessageSender.MessageQueue.Count); } @@ -179,7 +179,7 @@ public void WhenSendingMessaged_SentDataIsCorrect() var message2 = GetMessage(); m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients); m_MessagingSystem.SendMessage(message2, NetworkDelivery.Reliable, m_Clients); - + m_MessagingSystem.ProcessSendQueues(); var reader = new FastBufferReader(m_MessageSender.MessageQueue[0], Allocator.Temp); using (reader) @@ -191,13 +191,13 @@ public void WhenSendingMessaged_SentDataIsCorrect() ); reader.ReadValue(out BatchHeader header); Assert.AreEqual(2, header.BatchSize); - + reader.ReadValue(out MessageHeader messageHeader); Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader.MessageType); Assert.AreEqual(UnsafeUtility.SizeOf(), messageHeader.MessageSize); reader.ReadValue(out TestMessage receivedMessage); Assert.AreEqual(message, receivedMessage); - + reader.ReadValue(out MessageHeader messageHeader2); Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader2.MessageType); Assert.AreEqual(UnsafeUtility.SizeOf(), messageHeader2.MessageSize); @@ -206,4 +206,4 @@ public void WhenSendingMessaged_SentDataIsCorrect() } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs index 64081420d3..e6236196f4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs @@ -1,9 +1,9 @@ -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.EditorTests { - class NopMessageSender : IMessageSender + internal class NopMessageSender : IMessageSender { public void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData) { } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index 88f55a5551..fa067d73a6 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using Unity.Netcode.EditorTests; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index 87b0154ee5..e8c4cf3f29 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -317,26 +317,20 @@ public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled() Assert.Throws(() => { - using (var bitReader = reader.EnterBitwiseContext()) - { - bitReader.ReadBit(out bool b); - } + using var bitReader = reader.EnterBitwiseContext(); + bitReader.ReadBit(out bool b); }); Assert.Throws(() => { - using (var bitReader = reader.EnterBitwiseContext()) - { - bitReader.ReadBits(out byte b, 1); - } + using var bitReader = reader.EnterBitwiseContext(); + bitReader.ReadBits(out byte b, 1); }); Assert.Throws(() => { - using (var bitReader = reader.EnterBitwiseContext()) - { - bitReader.ReadBits(out ulong ul, 1); - } + using var bitReader = reader.EnterBitwiseContext(); + bitReader.ReadBits(out ulong ul, 1); }); Assert.AreEqual(0, reader.Position); @@ -344,21 +338,19 @@ public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled() Assert.Throws(() => { Assert.IsTrue(reader.TryBeginRead(1)); - using (var bitReader = reader.EnterBitwiseContext()) + using var bitReader = reader.EnterBitwiseContext(); + ulong ul; + try { - ulong ul; - try - { - bitReader.ReadBits(out ul, 4); - bitReader.ReadBits(out ul, 4); - } - catch (OverflowException e) - { - Assert.Fail("Overflow exception was thrown too early."); - throw; - } bitReader.ReadBits(out ul, 4); + bitReader.ReadBits(out ul, 4); + } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; } + bitReader.ReadBits(out ul, 4); }); } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index cd51c356de..ab98d06da9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -270,34 +270,26 @@ public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled() Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBit(true); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBit(true); }); Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBit(false); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBit(false); }); Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBits(0b11111111, 1); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBits(0b11111111, 1); }); Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBits(0b11111111UL, 1); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBits(0b11111111UL, 1); }); Assert.AreEqual(0, writer.Position); @@ -310,20 +302,18 @@ public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled() Assert.Throws(() => { Assert.IsTrue(writer.TryBeginWrite(1)); - using (var bitWriter = writer.EnterBitwiseContext()) + using var bitWriter = writer.EnterBitwiseContext(); + try { - try - { - bitWriter.WriteBits(0b11111111UL, 4); - bitWriter.WriteBits(0b11111111UL, 4); - } - catch (OverflowException e) - { - Assert.Fail("Overflow exception was thrown too early."); - throw; - } - bitWriter.WriteBits(0b11111111UL, 1); + bitWriter.WriteBits(0b11111111UL, 4); + bitWriter.WriteBits(0b11111111UL, 4); } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; + } + bitWriter.WriteBits(0b11111111UL, 1); }); } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 876cc4ab12..1847dd0954 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode.EditorTests diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 52a9180c1e..0f3e36e2e8 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -501,10 +501,8 @@ public void WhenCallingReadByteDuringBitwiseContext_InvalidOperationExceptionIsT using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } } @@ -516,11 +514,9 @@ public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIs using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - byte[] b = { 0, 1, 2 }; - Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); - } + using var context = emptyReader.EnterBitwiseContext(); + byte[] b = { 0, 1, 2 }; + Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } } @@ -532,10 +528,8 @@ public void WhenCallingReadValueWithUnmanagedTypeDuringBitwiseContext_InvalidOpe using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValue(out int i); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } } @@ -547,10 +541,8 @@ public void WhenCallingReadValueWithByteArrayDuringBitwiseContext_InvalidOperati using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); } } @@ -562,10 +554,8 @@ public void WhenCallingReadValueWithStringDuringBitwiseContext_InvalidOperationE using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValue(out string s); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); } } @@ -577,10 +567,8 @@ public void WhenCallingReadByteSafeDuringBitwiseContext_InvalidOperationExceptio using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadByteSafe(out byte b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadByteSafe(out byte b); }); } } @@ -592,11 +580,9 @@ public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExcepti using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - byte[] b = { 0, 1, 2 }; - Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); - } + using var context = emptyReader.EnterBitwiseContext(); + byte[] b = { 0, 1, 2 }; + Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); } } @@ -608,10 +594,8 @@ public void WhenCallingReadValueSafeWithUnmanagedTypeDuringBitwiseContext_Invali using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValueSafe(out int i); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValueSafe(out int i); }); } } @@ -623,10 +607,8 @@ public void WhenCallingReadValueSafeWithByteArrayDuringBitwiseContext_InvalidOpe using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValueSafe(out byte[] b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValueSafe(out byte[] b); }); } } @@ -638,10 +620,8 @@ public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperat using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValueSafe(out string s); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValueSafe(out string s); }); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 54dca7131a..f1d3897737 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -480,10 +480,8 @@ public void WhenCallingWriteByteDuringBitwiseContext_InvalidOperationExceptionIs using (writer) { writer.TryBeginWrite(100); - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteByte(1); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteByte(1); }); } } @@ -496,10 +494,8 @@ public void WhenCallingWriteBytesDuringBitwiseContext_InvalidOperationExceptionI { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } @@ -512,10 +508,8 @@ public void WhenCallingWriteValueWithUnmanagedTypeDuringBitwiseContext_InvalidOp { writer.TryBeginWrite(100); int i = 1; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValue(i); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValue(i); }); } } @@ -528,10 +522,8 @@ public void WhenCallingWriteValueWithByteArrayDuringBitwiseContext_InvalidOperat { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } @@ -545,10 +537,8 @@ public void WhenCallingWriteValueWithStringDuringBitwiseContext_InvalidOperation writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; int i = 1; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValue(""); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValue(""); }); } } @@ -560,10 +550,8 @@ public void WhenCallingWriteByteSafeDuringBitwiseContext_InvalidOperationExcepti using (writer) { writer.TryBeginWrite(100); - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteByteSafe(1); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteByteSafe(1); }); } } @@ -576,10 +564,8 @@ public void WhenCallingWriteBytesSafeDuringBitwiseContext_InvalidOperationExcept { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); } } @@ -592,10 +578,8 @@ public void WhenCallingWriteValueSafeWithUnmanagedTypeDuringBitwiseContext_Inval { writer.TryBeginWrite(100); int i = 1; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValueSafe(i); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValueSafe(i); }); } } @@ -608,10 +592,8 @@ public void WhenCallingWriteValueSafeWithByteArrayDuringBitwiseContext_InvalidOp { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); } } @@ -623,10 +605,8 @@ public void WhenCallingWriteValueSafeWithStringDuringBitwiseContext_InvalidOpera using (writer) { writer.TryBeginWrite(100); - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValueSafe(""); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValueSafe(""); }); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs index 55bc79bed0..f22ad31e64 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs @@ -1,8 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.IO; -using System.Text; using NUnit.Framework; using Unity.Collections; using UnityEngine; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs index 57261d544a..ca0ad1a9dc 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs @@ -1,8 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.IO; -using System.Text; using NUnit.Framework; using Unity.Collections; using UnityEngine; @@ -32,7 +30,7 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() ulong receivedMessageSender = 0; Guid receivedMessageContent; - FirstClient.CustomMessagingManager.OnUnnamedMessage += + FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { receivedMessageSender = sender; @@ -61,7 +59,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() ulong firstReceivedMessageSender = 0; Guid firstReceivedMessageContent; - FirstClient.CustomMessagingManager.OnUnnamedMessage += + FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { firstReceivedMessageSender = sender; @@ -71,7 +69,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() ulong secondReceivedMessageSender = 0; Guid secondReceivedMessageContent; - SecondClient.CustomMessagingManager.OnUnnamedMessage += + SecondClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { secondReceivedMessageSender = sender; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs index 4eb22f3582..3f75f0767d 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs @@ -48,12 +48,12 @@ public void NetworkObjectSceneSerializationFailure() // that we know will be thrown invalidNetworkObjectOffsets.Add(writer.Position); - networkObject.GlobalObjectIdHash = (uint) (i); + networkObject.GlobalObjectIdHash = (uint)(i); invalidNetworkObjectIdCount.Add(i); invalidNetworkObjects.Add(gameObject); - writer.WriteValueSafe((int) networkObject.gameObject.scene.handle); + writer.WriteValueSafe((int)networkObject.gameObject.scene.handle); // Serialize the invalid NetworkObject var sceneObject = networkObject.GetMessageSceneObject(0); var prePosition = writer.Position; @@ -80,11 +80,11 @@ public void NetworkObjectSceneSerializationFailure() Assert.IsNotNull(networkObject); - networkObject.GlobalObjectIdHash = (uint) (i + 4096); + networkObject.GlobalObjectIdHash = (uint)(i + 4096); networkObjectsToTest.Add(gameObject); - writer.WriteValueSafe((int) networkObject.gameObject.scene.handle); + writer.WriteValueSafe((int)networkObject.gameObject.scene.handle); // Handle populating the scenes loaded list var scene = networkObject.gameObject.scene; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs index a2b8ba39ac..360a05dd9b 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVarBufferCopyTest.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.IO; using NUnit.Framework; using UnityEngine.TestTools; @@ -25,7 +24,7 @@ public override bool IsDirty() { return Dirty; } - + public override void WriteDelta(ref FastBufferWriter writer) { writer.TryBeginWrite(FastBufferWriter.GetWriteSize(k_DummyValue) + 1); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs index 62922f52fb..0c7a1f87fe 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs @@ -11,8 +11,8 @@ namespace Unity.Netcode.RuntimeTests public struct FixedString32Struct : INetworkSerializable { public FixedString32 FixedString; - - public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation + + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation { serializer.SerializeValue(ref FixedString); } @@ -23,7 +23,7 @@ public struct TestStruct : INetworkSerializable public uint SomeInt; public bool SomeBool; - public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation { serializer.SerializeValue(ref SomeInt); serializer.SerializeValue(ref SomeBool); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs index d9390d71f6..ae0e7d4923 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/RpcQueueTests.cs @@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Collections.Generic; using UnityEngine; using UnityEngine.TestTools; using NUnit.Framework; @@ -21,7 +20,7 @@ public void Setup() // Create, instantiate, and host Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _)); } - + /// /// This tests the RPC Queue outbound and inbound buffer capabilities. /// diff --git a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs index f81a6a87c9..024a811eac 100644 --- a/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs +++ b/testproject/Assets/Tests/Manual/Scripts/RandomMovement.cs @@ -1,10 +1,9 @@ -using Unity.Collections; using UnityEngine; using Unity.Netcode; namespace TestProject.ManualTests { - + /// /// Used with GenericObjects to randomly move them around /// @@ -82,7 +81,7 @@ private void ChangeDirectionClientRpc(Vector3 direction) { m_Direction = direction; } - + private void OnCollisionStay(Collision collision) { if (IsServer) diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs b/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs index 9ba8902016..ec95df6703 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs @@ -11,7 +11,7 @@ public struct StatsInfoContainer : INetworkSerializable { public List StatValues; - public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation { if (serializer.IsReader) { diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index 6c9cb70d1f..30d7b7ae40 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using TestProject.RuntimeTests.Support; using UnityEngine; -using UnityEngine.PlayerLoop; using UnityEngine.TestTools; namespace TestProject.RuntimeTests diff --git a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs b/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs index f95b685d11..6bd133b510 100644 --- a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs +++ b/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; @@ -382,7 +381,7 @@ public class UserSerializableClass : INetworkSerializable public ulong MyulongValue; public List MyByteListValues; - public void NetworkSerialize(BufferSerializer serializer) where T: IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation { serializer.SerializeValue(ref MyintValue); serializer.SerializeValue(ref MyulongValue); @@ -396,7 +395,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T: IBuffer } else { - + serializer.GetFastBufferWriter().WriteBytesSafe(MyByteListValues.ToArray()); } } From 1e8761b9878f5c7ab3a8cb6e27edfa458eec8cd9 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 16:40:00 -0500 Subject: [PATCH 32/58] Corrected some incorrect xmldocs. --- .../Runtime/Core/NetworkObject.cs | 4 +- .../Runtime/Core/SnapshotSystem.cs | 17 ++++---- .../Runtime/Messaging/CustomMessageManager.cs | 2 +- .../NetworkVariable/INetworkVariable.cs | 41 ++----------------- .../NetworkVariable/NetworkVariable.cs | 4 +- .../SceneManagement/NetworkSceneManager.cs | 6 +-- .../Runtime/SceneManagement/SceneEventData.cs | 12 +++--- .../SceneManagement/SceneEventProgress.cs | 2 +- .../Runtime/Serialization/BytewiseUtility.cs | 4 +- .../Runtime/Serialization/FastBufferReader.cs | 6 +-- .../FastBufferReaderExtensions.cs | 10 +++++ .../Runtime/Serialization/FastBufferWriter.cs | 13 +++--- .../FastBufferWriterExtensions.cs | 25 ++++++++--- .../Runtime/Spawning/NetworkPrefabHandler.cs | 2 +- 14 files changed, 68 insertions(+), 80 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index c4f112706b..f4d3950445 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1069,8 +1069,8 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) /// Used to deserialize a serialized scene object which occurs /// when the client is approved or during a scene transition /// - /// inbound stream - /// reader for the stream + /// Deserialized scene object data + /// reader for the NetworkVariable data /// NetworkManager instance /// optional to use NetworkObject deserialized internal static NetworkObject AddSceneObject(in SceneObject sceneObject, ref FastBufferReader variableData, NetworkManager networkManager) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index eb61183220..38c712fe4a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -101,8 +101,6 @@ internal class Snapshot /// Constructor /// Allocated a MemoryStream to be reused for this Snapshot /// - /// The NetworkManaher this Snapshot uses. Needed upon receive to set Variables - /// Whether this Snapshot uses the tick as an index internal Snapshot() { // we ask for twice as many slots because there could end up being one free spot between each pair of slot used @@ -280,7 +278,7 @@ internal ClientData.SentSpawn GetDespawnData(in ClientData clientData, in Snapsh /// Read a received Entry /// Must match WriteEntry /// - /// The readed to read the entry from + /// Deserialized snapshot entry data internal Entry ReadEntry(SnapshotDataMessage.EntryData data) { Entry entry; @@ -354,18 +352,17 @@ internal void AllocateEntry(ref Entry entry, int index, int size) /// Must match WriteBuffer /// The stream is actually a memory stream and we seek to each variable position as we deserialize them /// - /// The NetworkReader to read our buffer of variables from - /// The stream to read our buffer of variables from + /// The message to pull the buffer from internal void ReadBuffer(in SnapshotDataMessage message) { - RecvBuffer = message.ReceiveMainBuffer.ToArray(); + RecvBuffer = message.ReceiveMainBuffer.ToArray(); // Note: Allocates } /// /// Read the snapshot index from a buffer /// Stores the entry. Allocates memory if needed. The actual buffer will be read later /// - /// The reader to read the index from + /// The message to read the index from internal void ReadIndex(in SnapshotDataMessage message) { Entry entry; @@ -880,7 +877,7 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) /// /// Write the snapshot index to a buffer /// - /// The buffer to write the index to + /// The message to write the index to private void WriteIndex(ref SnapshotDataMessage message) { message.Entries = new NativeArray(m_Snapshot.LastEntry, Allocator.TempJob); @@ -968,8 +965,8 @@ private unsafe void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBa /// Entry point when a Snapshot is received /// This is where we read and store the received snapshot /// - /// - /// The stream to read from + /// + /// The message to read from internal void HandleSnapshot(ulong clientId, in SnapshotDataMessage message) { // make sure we have a ClientData entry for each client diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index 3101be718d..6aa0a20d86 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -21,7 +21,7 @@ internal CustomMessagingManager(NetworkManager networkManager) /// Delegate used for incoming unnamed messages /// /// The clientId that sent the message - /// The stream containing the message data + /// The stream containing the message data public delegate void UnnamedMessageDelegate(ulong clientId, ref FastBufferReader reader); /// diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs index ce4298722a..18ab561108 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs @@ -41,61 +41,28 @@ public interface INetworkVariable /// /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer /// - /// The stream to write the dirty changes to - void WriteDelta(Stream stream); - - /// - /// Writes the complete state of the variable to the writer - /// - /// The stream to write the state to - void WriteField(Stream stream); - - /// - /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer - /// - /// The stream to write the dirty changes to + /// The stream to write the dirty changes to void WriteDelta(ref FastBufferWriter writer); /// /// Writes the complete state of the variable to the writer /// - /// The stream to write the state to + /// The stream to write the state to void WriteField(ref FastBufferWriter writer); - /// - /// Reads the complete state from the reader and applies it - /// - /// The stream to read the state from - /// The local network tick at which this var was written, on the machine it was written - /// The remote network tick at which this var was sent by the host - void ReadField(Stream stream); - /// /// Reads delta from the reader and applies them to the internal value /// - /// The stream to read the delta from + /// The stream to read the delta from /// Whether or not the delta should be kept as dirty or consumed - /// The local network tick at which this var was written, on the machine it was written - /// The remote network tick at which this var was sent by the host void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta); /// /// Reads the complete state from the reader and applies it /// - /// The stream to read the state from - /// The local network tick at which this var was written, on the machine it was written - /// The remote network tick at which this var was sent by the host + /// The stream to read the state from void ReadField(ref FastBufferReader reader); - /// - /// Reads delta from the reader and applies them to the internal value - /// - /// The stream to read the delta from - /// Whether or not the delta should be kept as dirty or consumed - /// The local network tick at which this var was written, on the machine it was written - /// The remote network tick at which this var was sent by the host - void ReadDelta(Stream stream, bool keepDirtyDelta); - /// /// Sets NetworkBehaviour the container belongs to. /// diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs index d2302f6749..6defd0d8d9 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs @@ -97,7 +97,7 @@ private protected void Set(T value) /// /// Writes the variable to the writer /// - /// The stream to write the value to + /// The stream to write the value to public override void WriteDelta(ref FastBufferWriter writer) { WriteField(ref writer); @@ -107,7 +107,7 @@ public override void WriteDelta(ref FastBufferWriter writer) /// /// Reads value from the reader and applies it /// - /// The stream to read the value from + /// The stream to read the value from /// Whether or not the container should keep the dirty delta, or mark the delta as consumed public override void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta) { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 83add8268f..c6c59ce508 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -83,7 +83,7 @@ public class SceneEvent /// /// Main class for managing network scenes when is enabled. - /// Uses the message to communicate between the server and client(s) + /// Uses the message to communicate between the server and client(s) /// public class NetworkSceneManager : IDisposable { @@ -118,6 +118,7 @@ public class NetworkSceneManager : IDisposable /// public event SceneEventDelegate OnSceneEvent; + /// /// Delegate declaration for the handler that provides /// an additional level of scene loading security and/or validation to assure the scene being loaded /// is valid scene to be loaded in the LoadSceneMode specified. @@ -282,7 +283,6 @@ internal NetworkSceneManager(NetworkManager networkManager) /// loading mode is "a valid scene to be loaded in the LoadSceneMode specified". /// /// index into ScenesInBuild - /// Name of the scene /// LoadSceneMode the scene is going to be loaded /// true (Valid) or false (Invalid) internal bool ValidateSceneBeforeLoading(uint sceneIndex, LoadSceneMode loadSceneMode) @@ -1472,7 +1472,7 @@ private void HandleServerSceneEvent(ulong clientId) /// Both Client and Server: Incoming scene event entry point /// /// client who sent the scene event - /// data associated with the scene event + /// data associated with the scene event internal void HandleSceneEvent(ulong clientId, ref FastBufferReader reader) { if (m_NetworkManager != null) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 2b6905b79f..2d9dc9b1ac 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -2,13 +2,14 @@ using System; using System.Linq; using Unity.Collections; +using Unity.Netcode.Messages; using UnityEngine.SceneManagement; namespace Unity.Netcode { /// - /// Used by for messages + /// Used by for messages /// Note: This is only when is enabled /// public class SceneEventData : IDisposable @@ -305,7 +306,7 @@ private int SortNetworkObjects(NetworkObject first, NetworkObject second) /// Client and Server Side: /// Serializes data based on the SceneEvent type () /// - /// to write the scene event data + /// to write the scene event data internal void Serialize(ref FastBufferWriter writer) { // Write the scene event type @@ -673,7 +674,6 @@ internal void WriteClientSynchronizationResults(ref FastBufferWriter writer) /// it is finished loading. The client will also build a list of NetworkObjects that it spawned during /// this process which will be used as part of the Event_Sync_Complete response. /// - /// /// internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { @@ -684,9 +684,9 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) for (int i = 0; i < newObjectsCount; i++) { - /// We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is - /// currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject - /// from the list of populated + // We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is + // currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject + // from the list of populated InternalBuffer.ReadValueSafe(out int handle); m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(handle); diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs index f9f2010217..338f0c9c91 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs @@ -34,7 +34,7 @@ public enum SceneEventProgressStatus SceneEventInProgress, /// /// Returned if the scene name used with - /// or is invalid + /// or is invalid /// InvalidSceneName, /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs index 0732fca31e..e0940c3e6a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -6,8 +6,8 @@ public static class BytewiseUtility { /// /// Helper function optimized for quickly copying small numbers of bytes. - /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 - /// Slower for amount > 8 + /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 + /// Slower for amount > 8 /// /// Pointer to the source value /// Pointer to the destination value diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 2170a6c99c..c0d77a6ac5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -707,7 +707,7 @@ public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) /// Read a value of type FixedUnmanagedArray from the buffer. /// /// The value to copy - /// Any unmanaged type + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadValue(out FixedUnmanagedArray value, int count) where TPropertyType : unmanaged @@ -734,12 +734,12 @@ public unsafe void ReadValue(out FixedUnmanagedArra /// /// Read a value of type FixedUnmanagedArray from the buffer. - /// + /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple reads at once by calling TryBeginRead. /// /// The value to copy - /// Any unmanaged type + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadValueSafe(out FixedUnmanagedArray value, int count) where TPropertyType : unmanaged diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 19b8b812a9..0e6a668c5e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -11,6 +11,7 @@ public static class FastBufferReaderExtensions /// Reads a boxed object in a standard format /// Named differently from other ReadValue methods to avoid accidental boxing /// + /// The reader to use to read the value /// The object to read /// The type to be read /// @@ -132,6 +133,7 @@ public static void ReadObject(this ref FastBufferReader reader, out object value /// /// Read a GameObject /// + /// The reader to use to read the value /// value to read public static void ReadValue(this ref FastBufferReader reader, out GameObject value) { @@ -157,6 +159,7 @@ public static void ReadValue(this ref FastBufferReader reader, out GameObject va /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple reads at once by calling TryBeginRead. /// + /// The reader to use to read the value /// value to read public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject value) { @@ -179,6 +182,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out GameObjec /// /// Read an array of GameObjects /// + /// The reader to use to read the value /// value to read public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject[] value) { @@ -193,6 +197,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out GameObjec /// /// Read a NetworkObject /// + /// The reader to use to read the value /// value to read public static void ReadValue(this ref FastBufferReader reader, out NetworkObject value) { @@ -218,6 +223,7 @@ public static void ReadValue(this ref FastBufferReader reader, out NetworkObject /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple reads at once by calling TryBeginRead. /// + /// The reader to use to read the value /// value to read public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject value) { @@ -240,6 +246,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkOb /// /// Read an array of NetworkObjects /// + /// The reader to use to read the value /// value to read public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject[] value) { @@ -254,6 +261,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkOb /// /// Read a NetworkBehaviour /// + /// The reader to use to read the value /// value to read public static void ReadValue(this ref FastBufferReader reader, out NetworkBehaviour value) { @@ -280,6 +288,7 @@ public static void ReadValue(this ref FastBufferReader reader, out NetworkBehavi /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple reads at once by calling TryBeginRead. /// + /// The reader to use to read the value /// value to read public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour value) { @@ -303,6 +312,7 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBe /// /// Read an array of NetworkBehaviours /// + /// The reader to use to read the value /// value to read public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour[] value) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 32686811b0..5c9db03925 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -379,7 +379,9 @@ public void WriteNetworkSerializable(in T value) where T : INetworkSerializab /// /// Write an array of INetworkSerializables /// - /// The value to write + /// The value to write + /// + /// /// public void WriteNetworkSerializable(INetworkSerializable[] array, int count = -1, int offset = 0) where T : INetworkSerializable { @@ -720,7 +722,7 @@ public unsafe void CopyFrom(ref FastBufferWriter other) /// Get the size required to write a FixedUnmanagedArray /// /// - /// + /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe int GetWriteSize(in FixedUnmanagedArray value, int count) @@ -734,7 +736,7 @@ public static unsafe int GetWriteSize(in FixedUnman /// Write a value of type FixedUnmanagedArray to the buffer. /// /// The value to copy - /// Any unmanaged type + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteValue(in FixedUnmanagedArray value, int count) where TPropertyType : unmanaged @@ -760,12 +762,12 @@ public unsafe void WriteValue(in FixedUnmanagedArra /// /// Write a value of type FixedUnmanagedArray to the buffer. - /// + /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// /// The value to copy - /// Any unmanaged type + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteValueSafe(in FixedUnmanagedArray value, int count) where TPropertyType : unmanaged @@ -823,7 +825,6 @@ public static unsafe int GetWriteSize(in T value) where T : unmanaged /// /// Get the size required to write an unmanaged value of type T /// - /// /// /// public static unsafe int GetWriteSize() where T : unmanaged diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs index 94240f61ac..2d56adb036 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -11,6 +11,7 @@ public static class FastBufferWriterExtensions /// Writes a boxed object in a standard format /// Named differently from other WriteValue methods to avoid accidental boxing /// + /// The writer to use to write the value /// The object to write /// /// If true, an extra byte will be written to indicate whether or not the value is null. @@ -133,6 +134,7 @@ public static int GetGameObjectWriteSize() /// /// Write a GameObject /// + /// The writer to use to write the value /// The value to write public static void WriteValue(this ref FastBufferWriter writer, in GameObject value) { @@ -153,6 +155,7 @@ public static void WriteValue(this ref FastBufferWriter writer, in GameObject va /// /// Write an array of GameObjects /// + /// The writer to use to write the value /// The value to write /// /// @@ -161,7 +164,7 @@ public static void WriteValue(this ref FastBufferWriter writer, GameObject[] val writer.WriteValue((int)value.Length); foreach (var item in value) { - writer.WriteValue(value); + writer.WriteValue(item); } } @@ -171,6 +174,7 @@ public static void WriteValue(this ref FastBufferWriter writer, GameObject[] val /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// + /// The writer to use to write the value /// The value to write public static void WriteValueSafe(this ref FastBufferWriter writer, in GameObject value) { @@ -194,6 +198,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, in GameObjec /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// + /// The writer to use to write the value /// The value to write /// /// @@ -202,7 +207,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject[] writer.WriteValueSafe((int)value.Length); foreach (var item in value) { - writer.WriteValueSafe(value); + writer.WriteValueSafe(item); } } @@ -229,6 +234,7 @@ public static int GetNetworkObjectWriteSize() /// /// Write a NetworkObject /// + /// The writer to use to write the value /// The value to write public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject value) { @@ -243,6 +249,7 @@ public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject /// /// Write an array of NetworkObjects /// + /// The writer to use to write the value /// The value to write /// /// @@ -251,7 +258,7 @@ public static void WriteValue(this ref FastBufferWriter writer, NetworkObject[] writer.WriteValue((int)value.Length); foreach (var item in value) { - writer.WriteValue(value); + writer.WriteValue(item); } } @@ -261,6 +268,7 @@ public static void WriteValue(this ref FastBufferWriter writer, NetworkObject[] /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// + /// The writer to use to write the value /// The value to write public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkObject value) { @@ -277,6 +285,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkOb /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// + /// The writer to use to write the value /// The value to write /// /// @@ -285,7 +294,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObjec writer.WriteValueSafe((int)value.Length); foreach (var item in value) { - writer.WriteValueSafe(value); + writer.WriteValueSafe(item); } } @@ -313,6 +322,7 @@ public static int GetNetworkBehaviourWriteSize() /// /// Write a NetworkBehaviour /// + /// The writer to use to write the value /// The value to write public static void WriteValue(this ref FastBufferWriter writer, in NetworkBehaviour value) { @@ -328,6 +338,7 @@ public static void WriteValue(this ref FastBufferWriter writer, in NetworkBehavi /// /// Write an array of NetworkBehaviours /// + /// The writer to use to write the value /// The value to write /// /// @@ -336,7 +347,7 @@ public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour writer.WriteValue((int)value.Length); foreach (var item in value) { - writer.WriteValue(value); + writer.WriteValue(item); } } @@ -346,6 +357,7 @@ public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// + /// The writer to use to write the value /// The value to write /// /// @@ -370,6 +382,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkBe /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking /// for multiple writes at once by calling TryBeginWrite. /// + /// The writer to use to write the value /// The value to write /// /// @@ -378,7 +391,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehav writer.WriteValueSafe((int)value.Length); foreach (var item in value) { - writer.WriteValueSafe(value); + writer.WriteValueSafe(item); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs index b15902ec57..ec8ca8d372 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs @@ -176,7 +176,7 @@ public bool RemoveHandler(NetworkObject networkObject) /// /// Use the of the overridden network prefab asset to remove a registered class that implements the interface. /// - /// of the source NetworkPrefab that was being overridden + /// of the source NetworkPrefab that was being overridden /// true (success) or false (failure) public bool RemoveHandler(uint globalObjectIdHash) { From 89e66e8a4b1c41fe5f4dd3b05c58b3b12d088a89 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 19:00:46 -0500 Subject: [PATCH 33/58] -Added some tests for serializing RPC parameters using FastBufferReader/FastBufferWriter extension methods -Fixed invalid IL code generated when using an extension method to serialize an array. --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 10 +- ...ble.cs => RpcUserSerializableTypesTest.cs} | 366 +++++++++++++++++- ...a => RpcUserSerializableTypesTest.cs.meta} | 0 3 files changed, 372 insertions(+), 4 deletions(-) rename testproject/Assets/Tests/Runtime/{RpcINetworkSerializable.cs => RpcUserSerializableTypesTest.cs} (51%) rename testproject/Assets/Tests/Runtime/{RpcINetworkSerializable.cs.meta => RpcUserSerializableTypesTest.cs.meta} (100%) diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 65aab8b0d8..a77ce9589a 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -979,9 +979,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); var method = methodRef.Resolve(); var checkParameter = method.Parameters[0]; + var isExtensionMethod = false; if (checkParameter.ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve()) { + isExtensionMethod = true; checkParameter = method.Parameters[1]; } if (checkParameter.IsIn) @@ -993,12 +995,16 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); } // Special handling for WriteValue() on arrays and strings since they have additional arguments. - if (paramType.IsArray) + if (paramType.IsArray + && ((!isExtensionMethod && methodRef.Parameters.Count == 3) + || (isExtensionMethod && methodRef.Parameters.Count == 4))) { instructions.Add(processor.Create(OpCodes.Ldc_I4_M1)); instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); } - else if (paramType == typeSystem.String) + else if (paramType == typeSystem.String + && ((!isExtensionMethod && methodRef.Parameters.Count == 2) + || (isExtensionMethod && methodRef.Parameters.Count == 3))) { instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); } diff --git a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs similarity index 51% rename from testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs rename to testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs index 6bd133b510..5459fa29bb 100644 --- a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs +++ b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs @@ -8,7 +8,7 @@ namespace TestProject.RuntimeTests { - public class RpcINetworkSerializable : BaseMultiInstanceTest + public class RpcUserSerializableTypesTest : BaseMultiInstanceTest { private UserSerializableClass m_UserSerializableClass; private List m_UserSerializableClassArray; @@ -91,6 +91,194 @@ public IEnumerator NetworkSerializableTest() m_ServerNetworkManager.Shutdown(); } + /// + /// Tests that INetworkSerializable can be used through RPCs by a user + /// + /// + [UnityTest] + public IEnumerator ExtensionMethodRpcTest() + { + m_FinishedTest = false; + var startTime = Time.realtimeSinceStartup; + + yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab => + { + playerPrefab.AddComponent(); + }); + + // [Client-Side] We only need to get the client side Player's NetworkObject so we can grab that instance of the TestSerializationComponent + var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult)); + var clientSideNetworkBehaviourClass = clientClientPlayerResult.Result.gameObject.GetComponent(); + + var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult)); + var serverSideNetworkBehaviourClass = serverClientPlayerResult.Result.gameObject.GetComponent(); + + var obj = new MyObject(256); + var obj2 = new MySharedObjectReferencedById(256); + bool clientMyObjCalled = false; + bool clientMySharedObjCalled = true; + bool serverMyObjCalled = false; + bool serverMySharedObjCalled = true; + clientSideNetworkBehaviourClass.OnMyObjectUpdated = (receivedObj) => + { + Assert.AreEqual(obj.I, receivedObj.I); + Assert.AreNotSame(obj, receivedObj); + clientMyObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + serverSideNetworkBehaviourClass.OnMyObjectUpdated = (receivedObj) => + { + Assert.AreEqual(obj.I, receivedObj.I); + Assert.AreNotSame(obj, receivedObj); + serverMyObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + clientSideNetworkBehaviourClass.OnMySharedObjectReferencedByIdUpdated = (receivedObj) => + { + Assert.AreSame(obj2, receivedObj); + clientMySharedObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + serverSideNetworkBehaviourClass.OnMySharedObjectReferencedByIdUpdated = (receivedObj) => + { + Assert.AreSame(obj2, receivedObj); + serverMySharedObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + + clientSideNetworkBehaviourClass.SendMyObjectServerRpc(obj); + clientSideNetworkBehaviourClass.SendMySharedObjectReferencedByIdServerRpc(obj2); + + // Wait until the test has finished or we time out + var timeOutPeriod = Time.realtimeSinceStartup + 5; + var timedOut = false; + while (!m_FinishedTest) + { + if (Time.realtimeSinceStartup > timeOutPeriod) + { + timedOut = true; + break; + } + + yield return new WaitForSeconds(0.1f); + } + + // Verify the test passed + Assert.False(timedOut); + + // End of test + m_ClientNetworkManagers[0].Shutdown(); + m_ServerNetworkManager.Shutdown(); + } + + /// + /// Tests that INetworkSerializable can be used through RPCs by a user + /// + /// + [UnityTest] + public IEnumerator ExtensionMethodArrayRpcTest() + { + m_FinishedTest = false; + var startTime = Time.realtimeSinceStartup; + + yield return StartSomeClientsAndServerWithPlayers(true, NbClients, playerPrefab => + { + playerPrefab.AddComponent(); + }); + + // [Client-Side] We only need to get the client side Player's NetworkObject so we can grab that instance of the TestSerializationComponent + var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ClientNetworkManagers[0], clientClientPlayerResult)); + var clientSideNetworkBehaviourClass = clientClientPlayerResult.Result.gameObject.GetComponent(); + + var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult)); + var serverSideNetworkBehaviourClass = serverClientPlayerResult.Result.gameObject.GetComponent(); + + var objs = new[]{new MyObject(256), new MyObject(512)}; + var objs2 = new[]{new MySharedObjectReferencedById(256), new MySharedObjectReferencedById(512)}; + bool clientMyObjCalled = false; + bool clientMySharedObjCalled = true; + bool serverMyObjCalled = false; + bool serverMySharedObjCalled = true; + clientSideNetworkBehaviourClass.OnMyObjectUpdated = (receivedObjs) => + { + Assert.AreEqual(receivedObjs.Length, objs2.Length); + for (var i = 0; i < receivedObjs.Length; ++i) + { + Assert.AreEqual(objs[i].I, receivedObjs[i].I); + Assert.AreNotSame(objs[i], receivedObjs[i]); + } + clientMyObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + serverSideNetworkBehaviourClass.OnMyObjectUpdated = (receivedObjs) => + { + Assert.AreEqual(receivedObjs.Length, objs2.Length); + for (var i = 0; i < receivedObjs.Length; ++i) + { + Assert.AreEqual(objs[i].I, receivedObjs[i].I); + Assert.AreNotSame(objs[i], receivedObjs[i]); + } + serverMyObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + clientSideNetworkBehaviourClass.OnMySharedObjectReferencedByIdUpdated = (receivedObjs) => + { + Assert.AreEqual(receivedObjs.Length, objs2.Length); + for (var i = 0; i < receivedObjs.Length; ++i) + { + Assert.AreSame(objs2[i], receivedObjs[i]); + } + clientMySharedObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + serverSideNetworkBehaviourClass.OnMySharedObjectReferencedByIdUpdated = (receivedObjs) => + { + Assert.AreEqual(receivedObjs.Length, objs2.Length); + for (var i = 0; i < receivedObjs.Length; ++i) + { + Assert.AreSame(objs2[i], receivedObjs[i]); + } + serverMySharedObjCalled = true; + m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && + serverMySharedObjCalled; + }; + + clientSideNetworkBehaviourClass.SendMyObjectServerRpc(objs); + clientSideNetworkBehaviourClass.SendMySharedObjectReferencedByIdServerRpc(objs2); + + // Wait until the test has finished or we time out + var timeOutPeriod = Time.realtimeSinceStartup + 5; + var timedOut = false; + while (!m_FinishedTest) + { + if (Time.realtimeSinceStartup > timeOutPeriod) + { + timedOut = true; + break; + } + + yield return new WaitForSeconds(0.1f); + } + + // Verify the test passed + Assert.False(timedOut); + + // End of test + m_ClientNetworkManagers[0].Shutdown(); + m_ServerNetworkManager.Shutdown(); + } + /// /// Delegate handler invoked towards the end of the when the NetworkSerializableTest /// @@ -271,8 +459,13 @@ private void OnServerReceivedUserSerializableClassesUpdated(UserSerializableClas public class TestSerializationComponent : NetworkBehaviour { public delegate void OnSerializableClassUpdatedDelgateHandler(UserSerializableClass userSerializableClass); - public OnSerializableClassUpdatedDelgateHandler OnSerializableClassUpdated; + + public delegate void OnMySharedObjectReferencedByIdUpdatedDelgateHandler(MySharedObjectReferencedById obj); + public OnMySharedObjectReferencedByIdUpdatedDelgateHandler OnMySharedObjectReferencedByIdUpdated; + + public delegate void OnMyObjectUpdatedDelgateHandler(MyObject obj); + public OnMyObjectUpdatedDelgateHandler OnMyObjectUpdated; /// /// Starts the unit test and passes the UserSerializableClass from the client to the server @@ -317,6 +510,44 @@ private void SendClientSerializedDataClientRpc(UserSerializableClass userSeriali OnSerializableClassUpdated.Invoke(userSerializableClass); } } + + [ClientRpc] + public void SendMyObjectClientRpc(MyObject obj) + { + if (OnMyObjectUpdated != null) + { + OnMyObjectUpdated.Invoke(obj); + } + } + + [ClientRpc] + public void SendMySharedObjectReferencedByIdClientRpc(MySharedObjectReferencedById obj) + { + if (OnMySharedObjectReferencedByIdUpdated != null) + { + OnMySharedObjectReferencedByIdUpdated.Invoke(obj); + } + } + + [ServerRpc] + public void SendMyObjectServerRpc(MyObject obj) + { + if (OnMyObjectUpdated != null) + { + OnMyObjectUpdated.Invoke(obj); + } + SendMyObjectClientRpc(obj); + } + + [ServerRpc] + public void SendMySharedObjectReferencedByIdServerRpc(MySharedObjectReferencedById obj) + { + if (OnMySharedObjectReferencedByIdUpdated != null) + { + OnMySharedObjectReferencedByIdUpdated.Invoke(obj); + } + SendMySharedObjectReferencedByIdClientRpc(obj); + } } /// @@ -329,6 +560,12 @@ public class TestCustomTypesArrayComponent : NetworkBehaviour { public delegate void OnSerializableClassesUpdatedDelgateHandler(UserSerializableClass[] userSerializableClasses); + public delegate void OnMySharedObjectReferencedByIdUpdatedDelgateHandler(MySharedObjectReferencedById[] obj); + public OnMySharedObjectReferencedByIdUpdatedDelgateHandler OnMySharedObjectReferencedByIdUpdated; + + public delegate void OnMyObjectUpdatedDelgateHandler(MyObject[] obj); + public OnMyObjectUpdatedDelgateHandler OnMyObjectUpdated; + public OnSerializableClassesUpdatedDelgateHandler OnSerializableClassesUpdatedServerRpc; public OnSerializableClassesUpdatedDelgateHandler OnSerializableClassesUpdatedClientRpc; @@ -370,6 +607,45 @@ private void SendClientSerializedDataClientRpc(UserSerializableClass[] userSeria OnSerializableClassesUpdatedClientRpc.Invoke(userSerializableClasses); } } + + [ClientRpc] + public void SendMyObjectClientRpc(MyObject[] objs) + { + if (OnMyObjectUpdated != null) + { + OnMyObjectUpdated.Invoke(objs); + } + } + + [ClientRpc] + public void SendMySharedObjectReferencedByIdClientRpc(MySharedObjectReferencedById[] objs) + { + if (OnMySharedObjectReferencedByIdUpdated != null) + { + OnMySharedObjectReferencedByIdUpdated.Invoke(objs); + } + } + + [ServerRpc] + public void SendMyObjectServerRpc(MyObject[] objs) + { + if (OnMyObjectUpdated != null) + { + OnMyObjectUpdated.Invoke(objs); + } + SendMyObjectClientRpc(objs); + } + + + [ServerRpc] + public void SendMySharedObjectReferencedByIdServerRpc(MySharedObjectReferencedById[] objs) + { + if (OnMySharedObjectReferencedByIdUpdated != null) + { + OnMySharedObjectReferencedByIdUpdated.Invoke(objs); + } + SendMySharedObjectReferencedByIdClientRpc(objs); + } } /// @@ -405,5 +681,91 @@ public UserSerializableClass() MyByteListValues = new List(); } } + + public class MyObject + { + public int I; + + public MyObject(int i) + { + I = i; + } + } + + public class MySharedObjectReferencedById + { + public static Dictionary Values = + new Dictionary(); + public int I; + + public MySharedObjectReferencedById(int i) + { + I = i; + Values[I] = this; + } + } + + public static class TestSerializationExtensions + { + public static void ReadValueSafe(this ref FastBufferReader reader, out MyObject value) + { + reader.ReadValueSafe(out int i); + value = new MyObject(i); + } + + public static void WriteValueSafe(this ref FastBufferWriter writer, in MyObject value) + { + writer.WriteValueSafe(value.I); + } + + public static void ReadValueSafe(this ref FastBufferReader reader, out MySharedObjectReferencedById value) + { + reader.ReadValueSafe(out int i); + value = MySharedObjectReferencedById.Values[i]; + } + + public static void WriteValueSafe(this ref FastBufferWriter writer, MySharedObjectReferencedById value) + { + writer.WriteValueSafe(value.I); + } + public static void ReadValueSafe(this ref FastBufferReader reader, out MyObject[] values) + { + reader.ReadValueSafe(out int length); + values = new MyObject[length]; + for (var i = 0; i < length; ++i) + { + reader.ReadValueSafe(out values[i]); + } + } + + public static void WriteValueSafe(this ref FastBufferWriter writer, in MyObject[] values) + { + writer.WriteValueSafe(values.Length); + for (var i = 0; i < values.Length; ++i) + { + writer.WriteValueSafe(values[i]); + } + } + + public static void ReadValueSafe(this ref FastBufferReader reader, out MySharedObjectReferencedById[] values) + { + reader.ReadValueSafe(out int length); + values = new MySharedObjectReferencedById[length]; + for (var i = 0; i < length; ++i) + { + reader.ReadValueSafe(out values[i]); + } + } + + public static void WriteValueSafe(this ref FastBufferWriter writer, MySharedObjectReferencedById[] values) + { + writer.WriteValueSafe(values.Length); + for (var i = 0; i < values.Length; ++i) + { + writer.WriteValueSafe(values[i]); + } + } + + } } diff --git a/testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs.meta b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs.meta similarity index 100% rename from testproject/Assets/Tests/Runtime/RpcINetworkSerializable.cs.meta rename to testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs.meta From 7f95934e4b2bf925889b875c16978b5a48c19245 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 19:22:29 -0500 Subject: [PATCH 34/58] Fixed missing send queue information for the server on StartClient(). --- com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index fc92191b81..44d9db09aa 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -793,6 +793,7 @@ public SocketTasks StartClient() } Initialize(false); + m_MessagingSystem.ClientConnected(ServerClientId); var socketTasks = NetworkConfig.NetworkTransport.StartClient(); From c788567a36cde97057b46cca4d4ee2cabcc6b58e Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 20:13:49 -0500 Subject: [PATCH 35/58] Added ILPP compile-time check for INetworkMessage Receive() function. --- .../Editor/CodeGen/CodeGenHelpers.cs | 1 + .../Editor/CodeGen/INetworkMessageILPP.cs | 128 ++++++++++++++++++ .../Runtime/Messaging/MessagingSystem.cs | 4 +- .../Messaging/MessageRegistrationTests.cs | 96 +------------ 4 files changed, 132 insertions(+), 97 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs index d99ead74ec..5a271bfb74 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs @@ -17,6 +17,7 @@ internal static class CodeGenHelpers public const string RuntimeAssemblyName = "Unity.Netcode.Runtime"; public static readonly string NetworkBehaviour_FullName = typeof(NetworkBehaviour).FullName; + public static readonly string INetworkMessage_FullName = typeof(INetworkMessage).FullName; public static readonly string ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName; public static readonly string ClientRpcAttribute_FullName = typeof(ClientRpcAttribute).FullName; public static readonly string ServerRpcParams_FullName = typeof(ServerRpcParams).FullName; diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs new file mode 100644 index 0000000000..01a59f6ef9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using Unity.Collections; +using Unity.CompilationPipeline.Common.Diagnostics; +using Unity.CompilationPipeline.Common.ILPostProcessing; +using UnityEngine; +using UnityEngine.UIElements; +using MethodAttributes = Mono.Cecil.MethodAttributes; +using ParameterAttributes = Mono.Cecil.ParameterAttributes; +using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; + +namespace Unity.Netcode.Editor.CodeGen +{ + + internal sealed class INetworkMessageILPP : ILPPInterface + { + public override ILPPInterface GetInstance() => this; + + public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName); + + private readonly List m_Diagnostics = new List(); + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) + { + if (!WillProcess(compiledAssembly)) + { + return null; + } + + + m_Diagnostics.Clear(); + + // read + var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly, out var resolver); + if (assemblyDefinition == null) + { + m_Diagnostics.AddError($"Cannot read assembly definition: {compiledAssembly.Name}"); + return null; + } + + // process + var mainModule = assemblyDefinition.MainModule; + if (mainModule != null) + { + if (ImportReferences(mainModule)) + { + // process `INetworkMessage` types + mainModule.GetTypes() + .Where(t => t.HasInterface(CodeGenHelpers.INetworkMessage_FullName)) + .ToList() + .ForEach(b => ProcessINetworkMessage(b)); + } + else + { + m_Diagnostics.AddError($"Cannot import references into main module: {mainModule.Name}"); + } + } + else + { + m_Diagnostics.AddError($"Cannot get main module from assembly definition: {compiledAssembly.Name}"); + } + + // write + var pe = new MemoryStream(); + var pdb = new MemoryStream(); + + var writerParameters = new WriterParameters + { + SymbolWriterProvider = new PortablePdbWriterProvider(), + SymbolStream = pdb, + WriteSymbols = true + }; + + assemblyDefinition.Write(pe, writerParameters); + + return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics); + } + + + private TypeReference m_FastBufferReader_TypeRef; + private TypeReference m_NetworkContext_TypeRef; + + private bool ImportReferences(ModuleDefinition moduleDefinition) + { + m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(typeof(FastBufferReader)); + m_NetworkContext_TypeRef = moduleDefinition.ImportReference(typeof(NetworkContext)); + + return true; + } + + private void ProcessINetworkMessage(TypeDefinition typeDefinition) + { + var foundAValidMethod = false; + SequencePoint typeSequence = null; + foreach (var method in typeDefinition.Methods) + { + var resolved = method.Resolve(); + var methodSequence = resolved.DebugInformation.SequencePoints.FirstOrDefault(); + if (typeSequence == null || methodSequence.StartLine < typeSequence.StartLine) + { + typeSequence = methodSequence; + } + if (resolved.IsStatic && resolved.IsPublic && resolved.Name == "Receive" && resolved.Parameters.Count == 2 + && resolved.Parameters[0].ParameterType.IsByReference + && resolved.Parameters[0].ParameterType.GetElementType().Resolve() == + m_FastBufferReader_TypeRef.Resolve() + && resolved.Parameters[1].ParameterType.Resolve() == m_NetworkContext_TypeRef.Resolve() + && resolved.ReturnType == resolved.Module.TypeSystem.Void) + { + foundAValidMethod = true; + break; + } + } + + if (!foundAValidMethod) + { + m_Diagnostics.AddError(typeSequence, $"Class {typeDefinition.FullName} does not implement required function: `public static void Receive(ref FastBufferReader, NetworkContext)`"); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index 82bb11fee1..ca52657925 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -182,14 +182,14 @@ private void RegisterMessageType(Type messageType) if (method == null) { throw new InvalidMessageStructureException( - $"{messageType.Name}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); + $"{messageType.FullName}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); } var asDelegate = Delegate.CreateDelegate(typeof(MessageHandler), method, false); if (asDelegate == null) { throw new InvalidMessageStructureException( - $"{messageType.Name}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); + $"{messageType.FullName}: All INetworkMessage types must implement public static void Receive(ref FastBufferReader reader, NetworkContext context)"); } m_MessageHandlers[m_HighMessageType] = (MessageHandler)asDelegate; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs index c8b37b6598..5aba160e07 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -1,4 +1,4 @@ - + using NUnit.Framework; namespace Unity.Netcode.EditorTests @@ -145,99 +145,5 @@ public void WhenCreatingMessageSystem_BoundTypeMessageHandlersAreRegistered() systemThree.MessageHandlers[systemThree.GetMessageType(typeof(TestMessageFour))]); } } - - #region WhenCreatingMessageSystem_MissingReceiveHandlerThrowsException - private class BrokenSystemOwnerOne - { - - } - - [Bind(typeof(BrokenSystemOwnerOne))] - private struct TestMessageFive : INetworkMessage - { - public int A; - public int B; - public int C; - - public void Serialize(ref FastBufferWriter writer) - { - writer.WriteValue(this); - } - } - - [Test] - public void WhenCreatingMessageSystem_MissingReceiveHandlerThrowsException() - { - var owner = new BrokenSystemOwnerOne(); - var sender = new NopMessageSender(); - Assert.Throws(() => new MessagingSystem(sender, owner)); - } - #endregion - - #region WhenCreatingMessageSystem_ReceiveHandlerWithIncorrectParametersThrowsException - private class BrokenSystemOwnerTwo - { - - } - - [Bind(typeof(BrokenSystemOwnerTwo))] - private struct TestMessageSix : INetworkMessage - { - public int A; - public int B; - public int C; - - public void Serialize(ref FastBufferWriter writer) - { - writer.WriteValue(this); - } - - public static void Receive(ref FastBufferReader reader) - { - - } - } - - [Test] - public void WhenCreatingMessageSystem_ReceiveHandlerWithIncorrectParametersThrowsException() - { - var owner = new BrokenSystemOwnerTwo(); - var sender = new NopMessageSender(); - Assert.Throws(() => new MessagingSystem(sender, owner)); - } - #endregion - - #region WhenCreatingMessageSystem_ReceiveHandlerWithMissingRefSpecifierThrowsException - private class BrokenSystemOwnerThree - { - - } - - [Bind(typeof(BrokenSystemOwnerThree))] - private struct TestMessageSeven : INetworkMessage - { - public int A; - public int B; - public int C; - - public void Serialize(ref FastBufferWriter writer) - { - writer.WriteValue(this); - } - - public static void Receive(FastBufferReader reader, NetworkContext context) - { - - } - } - - [Test] - public void WhenCreatingMessageSystem_ReceiveHandlerWithMissingRefSpecifierThrowsException() - { - var owner = new BrokenSystemOwnerThree(); - var sender = new NopMessageSender(); - Assert.Throws(() => new MessagingSystem(sender, owner)); - } - #endregion } } From eb264753becab3c601100d4db2f6d8432cf9f253 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 14 Sep 2021 20:14:04 -0500 Subject: [PATCH 36/58] Missed the meta file. --- .../Editor/CodeGen/INetworkMessageILPP.cs.meta | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs.meta diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs.meta b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs.meta new file mode 100644 index 0000000000..47c5ba2801 --- /dev/null +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a754504752d649bb8131df8756bd764e +timeCreated: 1631666359 \ No newline at end of file From d2a2635c473d83263e3640b0d174fff60d889a81 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 12:58:15 -0500 Subject: [PATCH 37/58] Changed generic function lookup in ILPP to use Cecil instead of relying on system types. --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 141 ++++++++++-------- 1 file changed, 78 insertions(+), 63 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index a77ce9589a..57b3689d37 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -7,6 +7,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; +using Mono.Collections.Generic; using Unity.Collections; using Unity.CompilationPipeline.Common.Diagnostics; using Unity.CompilationPipeline.Common.ILPostProcessing; @@ -569,45 +570,54 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio return rpcAttribute; } - private MethodInfo GetWriteMethodViaSystemReflection(Type objectType, string name, Type paramType) + private MethodReference GetWriteMethodViaSystemReflection(string name, TypeReference paramType) { - foreach (var method in objectType.GetMethods()) + foreach (var method in m_FastBufferWriter_TypeRef.Resolve().Methods) { if (method.Name == name) { - var parameters = method.GetParameters(); + var parameters = method.Parameters; - if (parameters.Length == 0 || (parameters.Length > 1 && !parameters[1].IsOptional)) + if (parameters.Count == 0 || (parameters.Count > 1 && !parameters[1].IsOptional)) { continue; } - var checkType = paramType; + if (parameters[0].ParameterType.IsArray != paramType.IsArray) + { + continue; + } + + var checkType = paramType.Resolve(); if (paramType.IsArray) { - checkType = paramType.GetElementType(); + checkType = paramType.GetElementType().Resolve(); } - if ((parameters[0].ParameterType == checkType) || (parameters[0].ParameterType == checkType.MakeByRefType() && parameters[0].IsIn) && parameters[0].ParameterType.IsArray == paramType.IsArray) + if ( + (parameters[0].ParameterType.Resolve() == checkType + || (parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn))) { return method; } - if (method.IsGenericMethod) + if (method.HasGenericParameters && method.GenericParameters.Count == 1) { - var genericParamType = parameters[0].ParameterType; - if (genericParamType.IsByRef) + if (method.GenericParameters[0].HasConstraints) { - genericParamType = genericParamType.GetElementType(); - } - - if (genericParamType.IsArray == paramType.IsArray) - { - try - { - return method.MakeGenericMethod(checkType); - } - catch (Exception e) + foreach (var constraint in method.GenericParameters[0].Constraints) { + var resolvedConstraint = constraint.Resolve(); + + if ( + (resolvedConstraint.IsInterface && + checkType.HasInterface(resolvedConstraint.FullName)) + || (resolvedConstraint.IsClass && + checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName))) + { + var instanceMethod = new GenericInstanceMethod(method); + instanceMethod.GenericArguments.Add(checkType); + return instanceMethod; + } } } } @@ -654,7 +664,7 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer } } - var systemType = Type.GetType(paramType.FullName); + /*var systemType = Type.GetType(paramType.FullName); if (systemType == null) { systemType = Type.GetType(assemblyQualifiedName); @@ -663,17 +673,17 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer throw new Exception("Couldn't find type for " + paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName); } - } + }*/ // Try NetworkSerializable first because INetworkSerializable may also be valid for WriteValueSafe // and that would cause boxing if so. - var systemMethod = GetWriteMethodViaSystemReflection(typeof(FastBufferWriter), "WriteNetworkSerializable", systemType); - if (systemMethod == null) + var typeMethod = GetWriteMethodViaSystemReflection("WriteNetworkSerializable", paramType); + if (typeMethod == null) { - systemMethod = GetWriteMethodViaSystemReflection(typeof(FastBufferWriter), "WriteValueSafe", systemType); + typeMethod = GetWriteMethodViaSystemReflection("WriteValueSafe", paramType); } - if (systemMethod != null) + if (typeMethod != null) { - methodRef = m_MainModule.ImportReference(systemMethod); + methodRef = m_MainModule.ImportReference(typeMethod); m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef; foundMethodRef = true; } @@ -681,45 +691,60 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer return foundMethodRef; } - - private static MethodInfo GetReadMethodViaSystemReflection(Type objectType, string name, Type paramType) + private MethodReference GetReadMethodViaSystemReflection(string name, TypeReference paramType) { - foreach (var method in objectType.GetMethods()) + foreach (var method in m_FastBufferReader_TypeRef.Resolve().Methods) { + var paramTypeDef = paramType.Resolve(); if (method.Name == name) { - var parameters = method.GetParameters(); - if (parameters.Length == 0 || (parameters.Length > 1 && !parameters[1].IsOptional)) + var parameters = method.Parameters; + + if (parameters.Count == 0 || (parameters.Count > 1 && !parameters[1].IsOptional)) { continue; } - var checkType = paramType; + if (!parameters[0].IsOut) + { + return null; + } + + var methodParam = ((ByReferenceType) parameters[0].ParameterType).ElementType; + + if (methodParam.IsArray != paramType.IsArray) + { + continue; + } + + var checkType = paramType.Resolve(); if (paramType.IsArray) { - checkType = paramType.GetElementType(); + checkType = paramType.GetElementType().Resolve(); } - if ((parameters[0].ParameterType == checkType.MakeByRefType() && parameters[0].IsOut) && parameters[0].ParameterType.IsArray == paramType.IsArray) + if (methodParam.Resolve() == checkType.Resolve() || methodParam.Resolve() == checkType.MakeByReferenceType().Resolve()) { return method; } - if (method.IsGenericMethod) + if (method.HasGenericParameters && method.GenericParameters.Count == 1) { - var genericParamType = parameters[0].ParameterType; - if (genericParamType.IsByRef) - { - genericParamType = genericParamType.GetElementType(); - } - if (genericParamType.IsArray == paramType.IsArray) + if (method.GenericParameters[0].HasConstraints) { - try + foreach (var constraint in method.GenericParameters[0].Constraints) { - return method.MakeGenericMethod(checkType); - } - catch (Exception e) - { - + var resolvedConstraint = constraint.Resolve(); + + if ( + (resolvedConstraint.IsInterface && + checkType.HasInterface(resolvedConstraint.FullName)) + || (resolvedConstraint.IsClass && + checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName))) + { + var instanceMethod = new GenericInstanceMethod(method); + instanceMethod.GenericArguments.Add(checkType); + return instanceMethod; + } } } } @@ -751,26 +776,16 @@ private bool GetReadMethodForParameter(TypeReference paramType, out MethodRefere } } - var systemType = Type.GetType(paramType.FullName); - if (systemType == null) - { - systemType = Type.GetType(assemblyQualifiedName); - if (systemType == null) - { - throw new Exception("Couldn't find type for " + paramType.FullName + ", " + - paramType.Resolve().Module.Assembly.FullName); - } - } // Try NetworkSerializable first because INetworkSerializable may also be valid for ReadValueSafe // and that would cause boxing if so. - var systemMethod = GetReadMethodViaSystemReflection(typeof(FastBufferReader), "ReadNetworkSerializable", systemType); - if (systemMethod == null) + var typeMethod = GetReadMethodViaSystemReflection("ReadNetworkSerializable", paramType); + if (typeMethod == null) { - systemMethod = GetReadMethodViaSystemReflection(typeof(FastBufferReader), "ReadValueSafe", systemType); + typeMethod = GetReadMethodViaSystemReflection("ReadValueSafe", paramType); } - if (systemMethod != null) + if (typeMethod != null) { - methodRef = m_MainModule.ImportReference(systemMethod); + methodRef = m_MainModule.ImportReference(typeMethod); m_FastBufferReader_ReadValue_MethodRefs[assemblyQualifiedName] = methodRef; foundMethodRef = true; } From 3d176ee88b71f5fa547043a90e9f001af1674d7d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 13:31:33 -0500 Subject: [PATCH 38/58] Changed scene management delivery type to ReliableFragmentedSequenced to support larger messages. --- .../Runtime/SceneManagement/NetworkSceneManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index c6c59ce508..99319c276d 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -87,8 +87,7 @@ public class SceneEvent /// public class NetworkSceneManager : IDisposable { - private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableSequenced; - private const NetworkUpdateStage k_NetworkUpdateStage = NetworkUpdateStage.EarlyUpdate; + private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableFragmentedSequenced; // Used to be able to turn re-synchronization off for future snapshot development purposes. internal static bool DisableReSynchronization; From fa848a42041106f90dae623b216e59ef0469ecbe Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 14:45:30 -0500 Subject: [PATCH 39/58] Fixed DontDestroyWithOwner throwing an exception on disconnect, fixed memory leak if the server forcibly disconnects a client --- com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index f900f76484..a6aac7cff2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1228,7 +1228,6 @@ private void HandleRawTransportPoll(NetworkEvent networkEvent, ulong clientId, A { NetworkLog.LogInfo($"Disconnect Event From {clientId}"); } - m_MessagingSystem.ClientDisconnected(clientId); if (IsServer) { @@ -1409,6 +1408,7 @@ private void OnClientDisconnectFromServer(ulong clientId) ConnectedClients.Remove(clientId); } + m_MessagingSystem.ClientDisconnected(clientId); } private void SyncTime() From 00c6f71c3008ef1a42c809f250a34e8ad62cf0a5 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 15:46:19 -0500 Subject: [PATCH 40/58] - Removed DynamicUnmanagedArray in favor of NativeList - Changed ulong[] to IEnumerable in ClientRpcParams --- .../Runtime/Core/NetworkBehaviour.cs | 9 +- .../Runtime/Core/NetworkManager.cs | 1 - .../Runtime/Messaging/MessagingSystem.cs | 66 ++++++----- .../Runtime/Messaging/RpcParams.cs | 7 +- .../Runtime/Utility/DynamicUnmanagedArray.cs | 104 ------------------ .../Utility/DynamicUnmanagedArray.cs.meta | 11 -- .../HybridScripts/RpcQueueManualTests.cs | 4 +- .../Assets/Tests/Runtime/RpcTestsAutomated.cs | 2 + 8 files changed, 55 insertions(+), 149 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index fa77cd72cb..37e6a95033 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -112,7 +112,14 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, if (sendParams.Send.TargetClientIds != null) { - messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIds, true); + // Copy into a localArray because SendMessage doesn't take IEnumerable, only IReadOnlyList + ulong* localArray = stackalloc ulong[sendParams.Send.TargetClientIds.Count()]; + var idx = 0; + foreach (var clientId in sendParams.Send.TargetClientIds) + { + localArray[idx++] = clientId; + } + messageSize = NetworkManager.SendMessage(message, networkDelivery, localArray, idx, true); } else if (sendParams.Send.TargetClientIdsNativeArray != null) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index a6aac7cff2..53c950c091 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -793,7 +793,6 @@ public SocketTasks StartClient() } Initialize(false); - m_MessagingSystem.ClientConnected(ServerClientId); var socketTasks = NetworkConfig.NetworkTransport.StartClient(); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index ca52657925..86b0efe292 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -43,13 +43,13 @@ public SendQueueItem(NetworkDelivery delivery, int writerSize, Allocator writerA #endregion #region Private Members - private DynamicUnmanagedArray m_IncomingMessageQueue = new DynamicUnmanagedArray(16); + private NativeList m_IncomingMessageQueue = new NativeList(16, Allocator.Persistent); private MessageHandler[] m_MessageHandlers = new MessageHandler[255]; private Type[] m_ReverseTypeMap = new Type[255]; private Dictionary m_MessageTypes = new Dictionary(); - private NativeHashMap>> m_SendQueues = new NativeHashMap>>(64, Allocator.Persistent); + private Dictionary> m_SendQueues = new Dictionary>(); private List m_Hooks = new List(); @@ -143,15 +143,12 @@ public void Dispose() return; } - var keys = m_SendQueues.GetKeyArray(Allocator.Temp); - using (keys) + // Can't just iterate SendQueues or SendQueues.Keys because ClientDisconnected removes + // from the queue. + foreach (var kvp in m_SendQueues) { - foreach (var key in keys) - { - ClientDisconnected(key); - } + CleanupDisconnectedClient(kvp.Key); } - m_SendQueues.Dispose(); m_IncomingMessageQueue.Dispose(); m_Disposed = true; } @@ -264,7 +261,7 @@ private bool CanReceive(ulong clientId, Type messageType) return true; } - public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, float timestamp) + public unsafe void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, float timestamp) { var context = new NetworkContext { @@ -309,12 +306,12 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, } } - internal void ProcessIncomingMessageQueue() + internal unsafe void ProcessIncomingMessageQueue() { - for (var i = 0; i < m_IncomingMessageQueue.Count; ++i) + for (var i = 0; i < m_IncomingMessageQueue.Length; ++i) { // Avoid copies... - ref var item = ref m_IncomingMessageQueue.GetValueRef(i); + ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(i); HandleMessage(item.Header, ref item.Reader, item.SenderId, item.Timestamp); } @@ -323,19 +320,32 @@ internal void ProcessIncomingMessageQueue() internal void ClientConnected(ulong clientId) { - m_SendQueues[clientId] = DynamicUnmanagedArray.CreateRef(); + if (m_SendQueues.ContainsKey(clientId)) + { + return; + } + m_SendQueues[clientId] = new NativeList(16, Allocator.Persistent); } internal void ClientDisconnected(ulong clientId) + { + if (!m_SendQueues.ContainsKey(clientId)) + { + return; + } + CleanupDisconnectedClient(clientId); + m_SendQueues.Remove(clientId); + } + + private unsafe void CleanupDisconnectedClient(ulong clientId) { var queue = m_SendQueues[clientId]; - for (var i = 0; i < queue.Value.Count; ++i) + for (var i = 0; i < queue.Length; ++i) { - queue.Value.GetValueRef(i).Writer.Dispose(); + queue.GetUnsafeList()->ElementAt(i).Writer.Dispose(); } - DynamicUnmanagedArray.ReleaseRef(queue); - m_SendQueues.Remove(clientId); + queue.Dispose(); } private bool CanSend(ulong clientId, Type messageType, NetworkDelivery delivery) @@ -376,26 +386,26 @@ internal unsafe int SendMessage(in TMessageType m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(TMessageType), delivery); } - ref var sendQueueItem = ref m_SendQueues[clientId].Value; - if (sendQueueItem.Count == 0) + var sendQueueItem = m_SendQueues[clientId]; + if (sendQueueItem.Length == 0) { sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, maxSize)); - sendQueueItem.GetValueRef(0).Writer.Seek(sizeof(BatchHeader)); + sendQueueItem.GetUnsafeList()->ElementAt(0).Writer.Seek(sizeof(BatchHeader)); } else { - ref var lastQueueItem = ref sendQueueItem.GetValueRef(sendQueueItem.Count - 1); + ref var lastQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1); if (lastQueueItem.NetworkDelivery != delivery || lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position < tmpSerializer.Length) { sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, maxSize)); - sendQueueItem.GetValueRef(sendQueueItem.Count - 1).Writer.Seek(sizeof(BatchHeader)); + sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader)); } } - ref var writeQueueItem = ref sendQueueItem.GetValueRef(sendQueueItem.Count - 1); + ref var writeQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1); writeQueueItem.Writer.TryBeginWrite(sizeof(MessageHeader) + tmpSerializer.Length); var header = new MessageHeader { @@ -479,15 +489,15 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong return SendMessage(message, delivery, new PointerListWrapper(clientIds, 1)); } - internal void ProcessSendQueues() + internal unsafe void ProcessSendQueues() { foreach (var kvp in m_SendQueues) { var clientId = kvp.Key; - ref var sendQueueItem = ref kvp.Value.Value; - for (var i = 0; i < sendQueueItem.Count; ++i) + var sendQueueItem = kvp.Value; + for (var i = 0; i < sendQueueItem.Length; ++i) { - ref var queueItem = ref sendQueueItem.GetValueRef(i); + ref var queueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(i); if (queueItem.BatchHeader.BatchSize == 0) { queueItem.Writer.Dispose(); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs index 33e69bb852..76880b26ba 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Unity.Collections; namespace Unity.Netcode @@ -20,9 +21,11 @@ public struct ServerRpcParams public struct ClientRpcSendParams { /// - /// ulong array version of target id list - use either this OR TargetClientIdsNativeArray + /// IEnumerable version of target id list - use either this OR TargetClientIdsNativeArray + /// Note: Even if you provide a value such as NativeArray, enumerating it will cause boxing. + /// If you want to avoid boxing, use TargetClientIdsNativeArray /// - public ulong[] TargetClientIds; + public IEnumerable TargetClientIds; /// /// NativeArray version of target id list - use either this OR TargetClientIds diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs deleted file mode 100644 index f451bd2fef..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Netcode -{ - public struct DynamicUnmanagedArray : IReadOnlyList, IDisposable where T : unmanaged - { - private unsafe T* m_Data; - private Allocator m_Allocator; - private int m_Length; - private int m_Capacity; - - public int Count => m_Length; - - private int Capacity => m_Capacity; - - public bool IsReadOnly => false; - - public unsafe DynamicUnmanagedArray(int capacity, Allocator allocator = Allocator.Persistent) - { - m_Data = (T*)UnsafeUtility.Malloc(capacity * sizeof(T), UnsafeUtility.AlignOf(), allocator); - m_Allocator = allocator; - m_Length = 0; - m_Capacity = capacity; - } - - public unsafe void Dispose() - { - UnsafeUtility.Free(m_Data, m_Allocator); - } - - public unsafe T this[int index] - { - get => m_Data[index]; - set => m_Data[index] = value; - } - - public unsafe ref T GetValueRef(int index) - { - return ref m_Data[index]; - } - - private unsafe void Resize() - { - m_Capacity *= 2; - var data = (T*)UnsafeUtility.Malloc(m_Capacity * sizeof(T), UnsafeUtility.AlignOf(), m_Allocator); - UnsafeUtility.MemCpy(data, m_Data, m_Length * sizeof(T)); - UnsafeUtility.Free(m_Data, m_Allocator); - m_Data = data; - } - - public unsafe void Add(T item) - { - if (m_Length == m_Capacity) - { - Resize(); - } - m_Data[m_Length++] = item; - } - - public unsafe T Pop() - { - return m_Data[--m_Length]; - } - - public void Clear() - { - m_Length = 0; - } - - public IEnumerator GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public static unsafe Ref> CreateRef(int capacity = 16) - { - var array = - (DynamicUnmanagedArray*)UnsafeUtility.Malloc( - sizeof(DynamicUnmanagedArray), - UnsafeUtility.AlignOf>(), Allocator.Persistent); - - array->m_Data = (T*)UnsafeUtility.Malloc(capacity * sizeof(T), UnsafeUtility.AlignOf(), Allocator.Persistent); - array->m_Allocator = Allocator.Persistent; - array->m_Length = 0; - array->m_Capacity = capacity; - return new Ref>(array); - } - - public static unsafe void ReleaseRef(Ref> array) - { - array.Value.Dispose(); - UnsafeUtility.Free(array.Ptr, Allocator.Persistent); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta deleted file mode 100644 index a069f33e73..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/DynamicUnmanagedArray.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 521cff8a9643c6c4ab6d9ec0d4eb9a87 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs index 9a54aa25dc..5727080df5 100644 --- a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs +++ b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs @@ -640,7 +640,7 @@ private void OnSendCounterServerRpc(int counter, ulong clientId, ServerRpcParams [ServerRpc(RequireOwnership = false)] private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) { - m_ClientRpcParamsMultiParameter.Send.TargetClientIds[0] = parameters.Receive.SenderClientId; + m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new []{parameters.Receive.SenderClientId}; OnSendNoParametersClientRpc(m_ClientRpcParamsMultiParameter); } @@ -652,7 +652,7 @@ private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) [ServerRpc(RequireOwnership = false)] private void OnSendMultiParametersServerRpc(int count, float floatValue, long longValue, ServerRpcParams parameters = default) { - m_ClientRpcParamsMultiParameter.Send.TargetClientIds[0] = parameters.Receive.SenderClientId; + m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new []{parameters.Receive.SenderClientId}; OnSendMultiParametersClientRpc(count, floatValue, longValue, m_ClientRpcParamsMultiParameter); } diff --git a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs index e5389ce4f1..f3811856da 100644 --- a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs +++ b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs @@ -5,6 +5,7 @@ using UnityEngine; using UnityEngine.TestTools; using TestProject.ManualTests; +using Unity.Collections; using Unity.Netcode.RuntimeTests; using Unity.Netcode; using Debug = UnityEngine.Debug; @@ -21,6 +22,7 @@ public class RpcTestsAutomated : BaseMultiInstanceTest [UnitySetUp] public override IEnumerator Setup() { + NativeLeakDetection.Mode = NativeLeakDetectionMode.EnabledWithStackTrace; yield break; } From 73a1e1f4cbf3a296849d22037a2733392a9ee807 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 18:44:43 -0500 Subject: [PATCH 41/58] standards.py --fix, plus some adjustments based on over-the-zoom-shoulder review with Fatih --- .../Editor/CodeGen/INetworkMessageILPP.cs | 9 --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 45 +++++------ .../Runtime/Core/NetworkBehaviour.cs | 29 ++------ .../Runtime/Core/NetworkManager.cs | 18 +++-- .../Runtime/Core/NetworkObject.cs | 74 ++++++++----------- .../Messaging/Messages/ParentSyncMessage.cs | 6 +- .../Runtime/Messaging/Messages/RpcMessage.cs | 28 +++---- .../Runtime/Messaging/MessagingSystem.cs | 29 ++++++-- .../Collections/NetworkList.cs | 2 +- .../NetworkVariable/INetworkVariable.cs | 72 ------------------ .../NetworkVariable/INetworkVariable.cs.meta | 11 --- .../NetworkVariable/NetworkVariableBase.cs | 1 - .../Runtime/Serialization/BytePacker.cs | 8 -- .../Runtime/Serialization/ByteUnpacker.cs | 11 --- .../Runtime/Spawning/NetworkSpawnManager.cs | 2 +- .../Messaging/MessageRegistrationTests.cs | 2 +- .../HybridScripts/RpcQueueManualTests.cs | 4 +- .../Runtime/RpcUserSerializableTypesTest.cs | 26 +++---- 18 files changed, 125 insertions(+), 252 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs index 01a59f6ef9..e528cc8b2e 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs @@ -1,19 +1,10 @@ -using System; using System.IO; using System.Linq; using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; -using Unity.Collections; using Unity.CompilationPipeline.Common.Diagnostics; using Unity.CompilationPipeline.Common.ILPostProcessing; -using UnityEngine; -using UnityEngine.UIElements; -using MethodAttributes = Mono.Cecil.MethodAttributes; -using ParameterAttributes = Mono.Cecil.ParameterAttributes; using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; namespace Unity.Netcode.Editor.CodeGen diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 57b3689d37..60549d88d4 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -7,7 +7,6 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -using Mono.Collections.Generic; using Unity.Collections; using Unity.CompilationPipeline.Common.Diagnostics; using Unity.CompilationPipeline.Common.ILPostProcessing; @@ -570,7 +569,7 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio return rpcAttribute; } - private MethodReference GetWriteMethodViaSystemReflection(string name, TypeReference paramType) + private MethodReference GetFastBufferWriterWriteMethod(string name, TypeReference paramType) { foreach (var method in m_FastBufferWriter_TypeRef.Resolve().Methods) { @@ -595,7 +594,7 @@ private MethodReference GetWriteMethodViaSystemReflection(string name, TypeRefer } if ( - (parameters[0].ParameterType.Resolve() == checkType + (parameters[0].ParameterType.Resolve() == checkType || (parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn))) { return method; @@ -607,7 +606,7 @@ private MethodReference GetWriteMethodViaSystemReflection(string name, TypeRefer foreach (var constraint in method.GenericParameters[0].Constraints) { var resolvedConstraint = constraint.Resolve(); - + if ( (resolvedConstraint.IsInterface && checkType.HasInterface(resolvedConstraint.FullName)) @@ -664,22 +663,12 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer } } - /*var systemType = Type.GetType(paramType.FullName); - if (systemType == null) - { - systemType = Type.GetType(assemblyQualifiedName); - if (systemType == null) - { - throw new Exception("Couldn't find type for " + paramType.FullName + ", " + - paramType.Resolve().Module.Assembly.FullName); - } - }*/ // Try NetworkSerializable first because INetworkSerializable may also be valid for WriteValueSafe // and that would cause boxing if so. - var typeMethod = GetWriteMethodViaSystemReflection("WriteNetworkSerializable", paramType); + var typeMethod = GetFastBufferWriterWriteMethod("WriteNetworkSerializable", paramType); if (typeMethod == null) { - typeMethod = GetWriteMethodViaSystemReflection("WriteValueSafe", paramType); + typeMethod = GetFastBufferWriterWriteMethod("WriteValueSafe", paramType); } if (typeMethod != null) { @@ -691,7 +680,7 @@ private bool GetWriteMethodForParameter(TypeReference paramType, out MethodRefer return foundMethodRef; } - private MethodReference GetReadMethodViaSystemReflection(string name, TypeReference paramType) + private MethodReference GetFastBufferReaderReadMethod(string name, TypeReference paramType) { foreach (var method in m_FastBufferReader_TypeRef.Resolve().Methods) { @@ -710,7 +699,7 @@ private MethodReference GetReadMethodViaSystemReflection(string name, TypeRefere return null; } - var methodParam = ((ByReferenceType) parameters[0].ParameterType).ElementType; + var methodParam = ((ByReferenceType)parameters[0].ParameterType).ElementType; if (methodParam.IsArray != paramType.IsArray) { @@ -734,7 +723,7 @@ private MethodReference GetReadMethodViaSystemReflection(string name, TypeRefere foreach (var constraint in method.GenericParameters[0].Constraints) { var resolvedConstraint = constraint.Resolve(); - + if ( (resolvedConstraint.IsInterface && checkType.HasInterface(resolvedConstraint.FullName)) @@ -778,10 +767,10 @@ private bool GetReadMethodForParameter(TypeReference paramType, out MethodRefere // Try NetworkSerializable first because INetworkSerializable may also be valid for ReadValueSafe // and that would cause boxing if so. - var typeMethod = GetReadMethodViaSystemReflection("ReadNetworkSerializable", paramType); + var typeMethod = GetFastBufferReaderReadMethod("ReadNetworkSerializable", paramType); if (typeMethod == null) { - typeMethod = GetReadMethodViaSystemReflection("ReadValueSafe", paramType); + typeMethod = GetFastBufferReaderReadMethod("ReadValueSafe", paramType); } if (typeMethod != null) { @@ -927,11 +916,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA } } - // var writer = new FastBufferWriter(1300, Allocator.Temp, 65536); + // var writer = new FastBufferWriter(1285, Allocator.Temp, 63985); instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300 - sizeof(byte) - sizeof(ulong) - sizeof(uint) - sizeof(ushort))); instructions.Add(processor.Create(OpCodes.Ldc_I4_2)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, 65536)); + instructions.Add(processor.Create(OpCodes.Ldc_I4, 64000 - sizeof(byte) - sizeof(ulong) - sizeof(uint) - sizeof(ushort))); instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Constructor)); var firstInstruction = processor.Create(OpCodes.Nop); @@ -1010,15 +999,15 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1)); } // Special handling for WriteValue() on arrays and strings since they have additional arguments. - if (paramType.IsArray - && ((!isExtensionMethod && methodRef.Parameters.Count == 3) + if (paramType.IsArray + && ((!isExtensionMethod && methodRef.Parameters.Count == 3) || (isExtensionMethod && methodRef.Parameters.Count == 4))) { instructions.Add(processor.Create(OpCodes.Ldc_I4_M1)); instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); } - else if (paramType == typeSystem.String - && ((!isExtensionMethod && methodRef.Parameters.Count == 2) + else if (paramType == typeSystem.String + && ((!isExtensionMethod && methodRef.Parameters.Count == 2) || (isExtensionMethod && methodRef.Parameters.Count == 3))) { instructions.Add(processor.Create(OpCodes.Ldc_I4_0)); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 37e6a95033..2b59ff4465 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Linq; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Messages; namespace Unity.Netcode @@ -16,7 +15,7 @@ public abstract class NetworkBehaviour : MonoBehaviour { #pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `protected` - public enum __RpcExecStage + internal enum __RpcExecStage #pragma warning restore IDE1006 // restore naming rule violation check { None = 0, @@ -33,7 +32,7 @@ public enum __RpcExecStage #pragma warning disable IDE1006 // disable naming rule violation check [NonSerialized] // RuntimeAccessModifiersILPP will make this `protected` - public __RpcExecStage __rpc_exec_stage = __RpcExecStage.None; + internal __RpcExecStage __rpc_exec_stage = __RpcExecStage.None; #pragma warning restore 414 // restore assigned but its value is never used #pragma warning restore IDE1006 // restore naming rule violation check @@ -47,7 +46,7 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: - if (writer.Length > 1300) + if (writer.Length > 1300 - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)) { throw new OverflowException("RPC parameters are too large for unreliable delivery."); } @@ -57,7 +56,7 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR var message = new RpcMessage { - Data = new RpcMessage.Metadata + Header = new RpcMessage.HeaderData { Type = RpcMessage.RpcType.Server, NetworkObjectId = NetworkObjectId, @@ -89,7 +88,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: - if (writer.Length > 1300) + if (writer.Length > 1300 - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)) { throw new OverflowException("RPC parameters are too large for unreliable delivery."); } @@ -99,7 +98,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, var message = new RpcMessage { - Data = new RpcMessage.Metadata + Header = new RpcMessage.HeaderData { Type = RpcMessage.RpcType.Client, NetworkObjectId = NetworkObjectId, @@ -125,9 +124,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, { // NativeArray doesn't implement required IReadOnlyList interface, but that's ok, pointer + length // will be more efficient anyway. - messageSize = NetworkManager.SendMessage(message, networkDelivery, - (ulong*)sendParams.Send.TargetClientIdsNativeArray.Value.GetUnsafePtr(), - sendParams.Send.TargetClientIdsNativeArray.Value.Length); + messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIdsNativeArray.Value); } else { @@ -507,7 +504,6 @@ internal void WriteNetworkVariableData(ref FastBufferWriter writer, ulong client { writer.WriteValueSafe((ushort)0); } - writer.WriteValueSafe((ushort)0x12AB); } } @@ -521,14 +517,8 @@ internal void SetNetworkVariableData(ref FastBufferReader reader) for (int j = 0; j < NetworkVariableFields.Count; j++) { reader.ReadValueSafe(out ushort varSize); - ushort magic; if (varSize == 0) { - reader.ReadValueSafe(out magic); - if (magic != (ushort)0x12AB) - { - NetworkLog.LogWarning($"Var data ended not on the magic value."); - } continue; } @@ -556,11 +546,6 @@ internal void SetNetworkVariableData(ref FastBufferReader reader) reader.Seek(readStartPos + varSize); } } - reader.ReadValueSafe(out magic); - if (magic != (ushort)0x12AB) - { - NetworkLog.LogWarning($"Var data ended not on the magic value."); - } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 53c950c091..776e93158c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -2,6 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Messages; using UnityEngine; #if UNITY_EDITOR @@ -1303,6 +1305,12 @@ public unsafe int SendMessage(in T message, NetworkDelivery delivery, return m_MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds); } + internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in NativeArray clientIds) + where T : INetworkMessage + { + return SendMessage(message, delivery, (ulong*)clientIds.GetUnsafePtr(), clientIds.Length); + } + public int SendMessage(in T message, NetworkDelivery delivery, ulong clientId, bool serverCanSendToServerId = false) where T : INetworkMessage { @@ -1529,11 +1537,11 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash) { ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key) }; - message.ObjectInfo.Metadata.Hash = playerPrefabHash; - message.ObjectInfo.Metadata.IsSceneObject = false; - message.ObjectInfo.Metadata.HasParent = false; - message.ObjectInfo.Metadata.IsPlayerObject = true; - message.ObjectInfo.Metadata.OwnerClientId = clientId; + message.ObjectInfo.Header.Hash = playerPrefabHash; + message.ObjectInfo.Header.IsSceneObject = false; + message.ObjectInfo.Header.HasParent = false; + message.ObjectInfo.Header.IsPlayerObject = true; + message.ObjectInfo.Header.OwnerClientId = clientId; SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 5cfdc2d5d2..a71dd581eb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -887,7 +887,7 @@ internal NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index) internal struct SceneObject { - public struct SceneObjectMetadata + public struct HeaderData { public ulong NetworkObjectId; public ulong OwnerClientId; @@ -900,13 +900,12 @@ public struct SceneObjectMetadata public bool IsReparented; } - public SceneObjectMetadata Metadata; + public HeaderData Header; - #region If(Metadata.HasParent) + //If(Metadata.HasParent) public ulong ParentObjectId; - #endregion - #region If(Metadata.HasTransform) + //If(Metadata.HasTransform) public struct TransformData { public Vector3 Position; @@ -914,15 +913,12 @@ public struct TransformData } public TransformData Transform; - #endregion - #region If(Metadata.IsReparented) + //If(Metadata.IsReparented) public bool IsLatestParentSet; - #region If(IsLatestParentSet) + //If(IsLatestParentSet) public ulong? LatestParent; - #endregion - #endregion public NetworkObject OwnerObject; public ulong TargetClientId; @@ -930,10 +926,10 @@ public struct TransformData public unsafe void Serialize(ref FastBufferWriter writer) { if (!writer.TryBeginWrite( - sizeof(SceneObjectMetadata) + - (Metadata.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) + - (Metadata.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) + - (Metadata.IsReparented + sizeof(HeaderData) + + (Header.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) + + (Header.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) + + (Header.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + (IsLatestParentSet ? FastBufferWriter.GetWriteSize() : 0) : 0))) @@ -941,19 +937,19 @@ public unsafe void Serialize(ref FastBufferWriter writer) throw new OverflowException("Could not serialize SceneObject: Out of buffer space."); } - writer.WriteValue(Metadata); + writer.WriteValue(Header); - if (Metadata.HasParent) + if (Header.HasParent) { writer.WriteValue(ParentObjectId); } - if (Metadata.HasTransform) + if (Header.HasTransform) { writer.WriteValue(Transform); } - if (Metadata.IsReparented) + if (Header.IsReparented) { writer.WriteValue(IsLatestParentSet); if (IsLatestParentSet) @@ -967,15 +963,15 @@ public unsafe void Serialize(ref FastBufferWriter writer) public unsafe void Deserialize(ref FastBufferReader reader) { - if (!reader.TryBeginRead(sizeof(SceneObjectMetadata))) + if (!reader.TryBeginRead(sizeof(HeaderData))) { throw new OverflowException("Could not deserialize SceneObject: Out of buffer space."); } - reader.ReadValue(out Metadata); + reader.ReadValue(out Header); if (!reader.TryBeginRead( - (Metadata.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) + - (Metadata.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) + - (Metadata.IsReparented + (Header.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) + + (Header.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) + + (Header.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + (IsLatestParentSet ? FastBufferWriter.GetWriteSize() : 0) : 0))) @@ -983,17 +979,17 @@ public unsafe void Deserialize(ref FastBufferReader reader) throw new OverflowException("Could not deserialize SceneObject: Out of buffer space."); } - if (Metadata.HasParent) + if (Header.HasParent) { reader.ReadValue(out ParentObjectId); } - if (Metadata.HasTransform) + if (Header.HasTransform) { reader.ReadValue(out Transform); } - if (Metadata.IsReparented) + if (Header.IsReparented) { reader.ReadValue(out IsLatestParentSet); if (IsLatestParentSet) @@ -1009,7 +1005,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) { var obj = new SceneObject { - Metadata = new SceneObject.SceneObjectMetadata + Header = new SceneObject.HeaderData { IsPlayerObject = IsPlayerObject, NetworkObjectId = NetworkObjectId, @@ -1030,12 +1026,12 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) if (parentNetworkObject) { - obj.Metadata.HasParent = true; + obj.Header.HasParent = true; obj.ParentObjectId = parentNetworkObject.NetworkObjectId; } if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId)) { - obj.Metadata.HasTransform = true; + obj.Header.HasTransform = true; obj.Transform = new SceneObject.TransformData { Position = transform.position, @@ -1044,7 +1040,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) } var (isReparented, latestParent) = GetNetworkParenting(); - obj.Metadata.IsReparented = isReparented; + obj.Header.IsReparented = isReparented; if (isReparented) { var isLatestParentSet = latestParent != null && latestParent.HasValue; @@ -1072,39 +1068,33 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, ref Fas Quaternion? rotation = null; ulong? parentNetworkId = null; - if (sceneObject.Metadata.HasTransform) + if (sceneObject.Header.HasTransform) { position = sceneObject.Transform.Position; rotation = sceneObject.Transform.Rotation; } - if (sceneObject.Metadata.HasParent) + if (sceneObject.Header.HasParent) { parentNetworkId = sceneObject.ParentObjectId; } //Attempt to create a local NetworkObject var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject( - sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.Hash, - sceneObject.Metadata.OwnerClientId, parentNetworkId, position, rotation, sceneObject.Metadata.IsReparented); + sceneObject.Header.IsSceneObject, sceneObject.Header.Hash, + sceneObject.Header.OwnerClientId, parentNetworkId, position, rotation, sceneObject.Header.IsReparented); - networkObject?.SetNetworkParenting(sceneObject.Metadata.IsReparented, sceneObject.LatestParent); + networkObject?.SetNetworkParenting(sceneObject.Header.IsReparented, sceneObject.LatestParent); if (networkObject == null) { // Log the error that the NetworkObject failed to construct - Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Metadata.Hash}."); + Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Header.Hash}."); // If we failed to load this NetworkObject, then skip past the network variable data variableData.ReadValueSafe(out ushort varSize); variableData.Seek(variableData.Position + varSize); - variableData.ReadValueSafe(out ushort magic); - if (magic != (ushort)0x12AB) - { - NetworkLog.LogWarning($"Var data ended not on the magic value."); - } - // We have nothing left to do here. return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs index 05827cc273..8ba0bb53ef 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs @@ -6,13 +6,11 @@ public struct ParentSyncMessage : INetworkMessage public bool IsReparented; - #region If(Metadata.IsReparented) + //If(Metadata.IsReparented) public bool IsLatestParentSet; - #region If(IsLatestParentSet) + //If(IsLatestParentSet) public ulong? LatestParent; - #endregion - #endregion public void Serialize(ref FastBufferWriter writer) { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs index 4246a6d66d..848ae021c4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs @@ -10,7 +10,7 @@ public enum RpcType : byte Client } - public struct Metadata + public struct HeaderData { public RpcType Type; public ulong NetworkObjectId; @@ -18,50 +18,50 @@ public struct Metadata public uint NetworkMethodId; } - public Metadata Data; + public HeaderData Header; public FastBufferWriter RPCData; public unsafe void Serialize(ref FastBufferWriter writer) { - if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Data) + RPCData.Length)) + if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Header) + RPCData.Length)) { throw new OverflowException("Not enough space in the buffer to store RPC data."); } - writer.WriteValue(Data); + writer.WriteValue(Header); writer.WriteBytes(RPCData.GetUnsafePtr(), RPCData.Length); } public static void Receive(ref FastBufferReader reader, NetworkContext context) { var message = new RpcMessage(); - if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.Data))) + if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.Header))) { throw new OverflowException("Not enough space in the buffer to read RPC data."); } - reader.ReadValue(out message.Data); + reader.ReadValue(out message.Header); message.Handle(ref reader, (NetworkManager)context.SystemOwner, context.SenderId); } public void Handle(ref FastBufferReader reader, NetworkManager networkManager, ulong senderId) { - if (NetworkManager.__rpc_func_table.ContainsKey(Data.NetworkMethodId)) + if (NetworkManager.__rpc_func_table.ContainsKey(Header.NetworkMethodId)) { - if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(Data.NetworkObjectId)) + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(Header.NetworkObjectId)) { return; } - var networkObject = networkManager.SpawnManager.SpawnedObjects[Data.NetworkObjectId]; + var networkObject = networkManager.SpawnManager.SpawnedObjects[Header.NetworkObjectId]; - var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(Data.NetworkBehaviourId); + var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(Header.NetworkBehaviourId); if (networkBehaviour == null) { return; } var rpcParams = new __RpcParams(); - switch (Data.Type) + switch (Header.Type) { case RpcType.Server: rpcParams.Server = new ServerRpcParams @@ -82,14 +82,14 @@ public void Handle(ref FastBufferReader reader, NetworkManager networkManager, u break; } - NetworkManager.__rpc_func_table[Data.NetworkMethodId](networkBehaviour, ref reader, rpcParams); + NetworkManager.__rpc_func_table[Header.NetworkMethodId](networkBehaviour, ref reader, rpcParams); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (NetworkManager.__rpc_name_table.TryGetValue(Data.NetworkMethodId, out var rpcMethodName)) + if (NetworkManager.__rpc_name_table.TryGetValue(Header.NetworkMethodId, out var rpcMethodName)) { networkManager.NetworkMetrics.TrackRpcReceived( senderId, - Data.NetworkObjectId, + Header.NetworkObjectId, rpcMethodName, networkBehaviour.__getTypeName(), reader.Length); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index 86b0efe292..1830632ec3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace Unity.Netcode @@ -16,7 +17,6 @@ public InvalidMessageStructureException(string issue) : base(issue) { } public class MessagingSystem : IDisposable { - #region Internal Types private struct ReceiveQueueItem { public FastBufferReader Reader; @@ -40,9 +40,7 @@ public SendQueueItem(NetworkDelivery delivery, int writerSize, Allocator writerA } internal delegate void MessageHandler(ref FastBufferReader reader, NetworkContext context); - #endregion - #region Private Members private NativeList m_IncomingMessageQueue = new NativeList(16, Allocator.Persistent); private MessageHandler[] m_MessageHandlers = new MessageHandler[255]; @@ -58,7 +56,6 @@ public SendQueueItem(NetworkDelivery delivery, int writerSize, Allocator writerA private IMessageSender m_MessageSender; private ulong m_LocalClientId; private bool m_Disposed; - #endregion internal Type[] MessageTypes => m_ReverseTypeMap; internal MessageHandler[] MessageHandlers => m_MessageHandlers; @@ -261,8 +258,14 @@ private bool CanReceive(ulong clientId, Type messageType) return true; } - public unsafe void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, float timestamp) + public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, ulong senderId, float timestamp) { + if (header.MessageType >= m_HighMessageType) + { + Debug.LogWarning($"Received a message with invalid message type value {header.MessageType}"); + reader.Dispose(); + return; + } var context = new NetworkContext { SystemOwner = m_Owner, @@ -489,6 +492,12 @@ internal unsafe int SendMessage(in T message, NetworkDelivery delivery, ulong return SendMessage(message, delivery, new PointerListWrapper(clientIds, 1)); } + internal unsafe int SendMessage(in T message, NetworkDelivery delivery, in NativeArray clientIds) + where T : INetworkMessage + { + return SendMessage(message, delivery, new PointerListWrapper((ulong*)clientIds.GetUnsafePtr(), clientIds.Length)); + } + internal unsafe void ProcessSendQueues() { foreach (var kvp in m_SendQueues) @@ -516,8 +525,14 @@ internal unsafe void ProcessSendQueues() #endif queueItem.Writer.WriteValue(queueItem.BatchHeader); - m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); - queueItem.Writer.Dispose(); + try + { + m_MessageSender.Send(clientId, queueItem.NetworkDelivery, ref queueItem.Writer); + } + finally + { + queueItem.Writer.Dispose(); + } for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) { diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index ce35aca17f..9775e63490 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -51,7 +51,7 @@ public NetworkList(IEnumerable values) foreach (var value in values) { m_List.Add(value); - + } } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs deleted file mode 100644 index 18ab561108..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.IO; - -namespace Unity.Netcode -{ - /// - /// Interface for network value containers - /// - public interface INetworkVariable - { - /// - /// Gets or sets the name of the network variable's instance - /// (MemberInfo) where it was declared. - /// - string Name { get; } - - /// - /// Resets the dirty state and marks the variable as synced / clean - /// - void ResetDirty(); - - /// - /// Gets Whether or not the container is dirty - /// - /// Whether or not the container is dirty - bool IsDirty(); - - /// - /// Gets Whether or not a specific client can write to the varaible - /// - /// The clientId of the remote client - /// Whether or not the client can write to the variable - bool CanClientWrite(ulong clientId); - - /// - /// Gets Whether or not a specific client can read to the varaible - /// - /// The clientId of the remote client - /// Whether or not the client can read to the variable - bool CanClientRead(ulong clientId); - - /// - /// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer - /// - /// The stream to write the dirty changes to - void WriteDelta(ref FastBufferWriter writer); - - /// - /// Writes the complete state of the variable to the writer - /// - /// The stream to write the state to - void WriteField(ref FastBufferWriter writer); - - /// - /// Reads delta from the reader and applies them to the internal value - /// - /// The stream to read the delta from - /// Whether or not the delta should be kept as dirty or consumed - void ReadDelta(ref FastBufferReader reader, bool keepDirtyDelta); - - /// - /// Reads the complete state from the reader and applies it - /// - /// The stream to read the state from - void ReadField(ref FastBufferReader reader); - - /// - /// Sets NetworkBehaviour the container belongs to. - /// - /// The behaviour the container behaves to - void SetNetworkBehaviour(NetworkBehaviour behaviour); - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta deleted file mode 100644 index 8ff8391fb8..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/INetworkVariable.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 87f1fd4778c2dab4b8bc02c738cade25 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index 4fe0bb7f67..14285770db 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -1,5 +1,4 @@ using System; -using System.IO; namespace Unity.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 494b9a80da..685cc911d9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -9,7 +9,6 @@ namespace Unity.Netcode /// public static class BytePacker { - #region Managed TypePacking /// /// Writes a boxed object in a packed format @@ -140,9 +139,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, throw new ArgumentException($"{nameof(BytePacker)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } - #endregion - #region Unmanaged Type Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -409,9 +406,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) } } #endif - #endregion - #region Bit Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -547,9 +542,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } #endif - #endregion - #region Private Methods private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) { if (value <= 240) @@ -612,6 +605,5 @@ private static unsafe ulong ToUlong(T value) where T : unmanaged ulong* asUlong = (ulong*)&value; return *asUlong; } - #endregion } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index 857829e126..98ed8c9320 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -6,7 +6,6 @@ namespace Unity.Netcode { public static class ByteUnpacker { - #region Managed TypePacking /// /// Reads a boxed object in a packed format @@ -135,9 +134,6 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } - #endregion - - #region Unmanaged Type Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -432,9 +428,6 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, out strin } } #endif - #endregion - - #region Bit Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -628,9 +621,6 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul value = returnValue >> 3; } #endif - #endregion - - #region Private Methods private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong value) { reader.ReadByteSafe(out byte firstByte); @@ -692,6 +682,5 @@ private static unsafe double ToDouble(T value) where T : unmanaged double* asDouble = (double*)&value; return *asDouble; } - #endregion } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 9a2634a5e2..a3f8a908b0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -303,7 +303,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkO networkObject.SetNetworkVariableData(ref variableData); - SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Metadata.NetworkObjectId, sceneObject.Metadata.IsSceneObject, sceneObject.Metadata.IsPlayerObject, sceneObject.Metadata.OwnerClientId, destroyWithScene); + SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene); } private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs index 5aba160e07..ccc950d186 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -1,4 +1,4 @@ - + using NUnit.Framework; namespace Unity.Netcode.EditorTests diff --git a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs index 5727080df5..0565cb076c 100644 --- a/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs +++ b/testproject/Assets/Tests/Manual/HybridScripts/RpcQueueManualTests.cs @@ -640,7 +640,7 @@ private void OnSendCounterServerRpc(int counter, ulong clientId, ServerRpcParams [ServerRpc(RequireOwnership = false)] private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) { - m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new []{parameters.Receive.SenderClientId}; + m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new[] { parameters.Receive.SenderClientId }; OnSendNoParametersClientRpc(m_ClientRpcParamsMultiParameter); } @@ -652,7 +652,7 @@ private void OnSendNoParametersServerRpc(ServerRpcParams parameters = default) [ServerRpc(RequireOwnership = false)] private void OnSendMultiParametersServerRpc(int count, float floatValue, long longValue, ServerRpcParams parameters = default) { - m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new []{parameters.Receive.SenderClientId}; + m_ClientRpcParamsMultiParameter.Send.TargetClientIds = new[] { parameters.Receive.SenderClientId }; OnSendMultiParametersClientRpc(count, floatValue, longValue, m_ClientRpcParamsMultiParameter); } diff --git a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs index 5459fa29bb..8be9ec992c 100644 --- a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs +++ b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs @@ -201,8 +201,8 @@ public IEnumerator ExtensionMethodArrayRpcTest() yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId), m_ServerNetworkManager, serverClientPlayerResult)); var serverSideNetworkBehaviourClass = serverClientPlayerResult.Result.gameObject.GetComponent(); - var objs = new[]{new MyObject(256), new MyObject(512)}; - var objs2 = new[]{new MySharedObjectReferencedById(256), new MySharedObjectReferencedById(512)}; + var objs = new[] { new MyObject(256), new MyObject(512) }; + var objs2 = new[] { new MySharedObjectReferencedById(256), new MySharedObjectReferencedById(512) }; bool clientMyObjCalled = false; bool clientMySharedObjCalled = true; bool serverMyObjCalled = false; @@ -253,7 +253,7 @@ public IEnumerator ExtensionMethodArrayRpcTest() m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && serverMySharedObjCalled; }; - + clientSideNetworkBehaviourClass.SendMyObjectServerRpc(objs); clientSideNetworkBehaviourClass.SendMySharedObjectReferencedByIdServerRpc(objs2); @@ -278,7 +278,7 @@ public IEnumerator ExtensionMethodArrayRpcTest() m_ClientNetworkManagers[0].Shutdown(); m_ServerNetworkManager.Shutdown(); } - + /// /// Delegate handler invoked towards the end of the when the NetworkSerializableTest /// @@ -460,10 +460,10 @@ public class TestSerializationComponent : NetworkBehaviour { public delegate void OnSerializableClassUpdatedDelgateHandler(UserSerializableClass userSerializableClass); public OnSerializableClassUpdatedDelgateHandler OnSerializableClassUpdated; - + public delegate void OnMySharedObjectReferencedByIdUpdatedDelgateHandler(MySharedObjectReferencedById obj); public OnMySharedObjectReferencedByIdUpdatedDelgateHandler OnMySharedObjectReferencedByIdUpdated; - + public delegate void OnMyObjectUpdatedDelgateHandler(MyObject obj); public OnMyObjectUpdatedDelgateHandler OnMyObjectUpdated; @@ -562,10 +562,10 @@ public class TestCustomTypesArrayComponent : NetworkBehaviour public delegate void OnMySharedObjectReferencedByIdUpdatedDelgateHandler(MySharedObjectReferencedById[] obj); public OnMySharedObjectReferencedByIdUpdatedDelgateHandler OnMySharedObjectReferencedByIdUpdated; - + public delegate void OnMyObjectUpdatedDelgateHandler(MyObject[] obj); public OnMyObjectUpdatedDelgateHandler OnMyObjectUpdated; - + public OnSerializableClassesUpdatedDelgateHandler OnSerializableClassesUpdatedServerRpc; public OnSerializableClassesUpdatedDelgateHandler OnSerializableClassesUpdatedClientRpc; @@ -685,7 +685,7 @@ public UserSerializableClass() public class MyObject { public int I; - + public MyObject(int i) { I = i; @@ -697,7 +697,7 @@ public class MySharedObjectReferencedById public static Dictionary Values = new Dictionary(); public int I; - + public MySharedObjectReferencedById(int i) { I = i; @@ -717,7 +717,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, in MyObject { writer.WriteValueSafe(value.I); } - + public static void ReadValueSafe(this ref FastBufferReader reader, out MySharedObjectReferencedById value) { reader.ReadValueSafe(out int i); @@ -746,7 +746,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, in MyObject[ writer.WriteValueSafe(values[i]); } } - + public static void ReadValueSafe(this ref FastBufferReader reader, out MySharedObjectReferencedById[] values) { reader.ReadValueSafe(out int length); @@ -765,7 +765,7 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, MySharedObje writer.WriteValueSafe(values[i]); } } - + } } From e4db7452086f3d529e498ccb2b01d3fc977a4e06 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 19:40:37 -0500 Subject: [PATCH 42/58] Not sure how standards.py --fix missed this the first time. --- .../Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs index 8be9ec992c..89607b8a18 100644 --- a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs +++ b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs @@ -151,7 +151,7 @@ public IEnumerator ExtensionMethodRpcTest() m_FinishedTest = clientMyObjCalled && clientMySharedObjCalled && serverMyObjCalled && serverMySharedObjCalled; }; - + clientSideNetworkBehaviourClass.SendMyObjectServerRpc(obj); clientSideNetworkBehaviourClass.SendMySharedObjectReferencedByIdServerRpc(obj2); From 73b207166b624219a04b2f1b73888a4542490837 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 20:43:44 -0500 Subject: [PATCH 43/58] increasing timeout in hopes it fixes test failure. --- .../Tests/Runtime/Helpers/RuntimeTestsHelpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/RuntimeTestsHelpers.cs b/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/RuntimeTestsHelpers.cs index 3982c26dcb..663de07e6c 100644 --- a/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/RuntimeTestsHelpers.cs +++ b/com.unity.netcode.adapter.utp/Tests/Runtime/Helpers/RuntimeTestsHelpers.cs @@ -11,9 +11,9 @@ public static class RuntimeTestsHelpers { // 50ms should be plenty enough for any network interaction to occur (even roundtrips). #if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_ANDROID - public const float MaxNetworkEventWaitTime = 0.35f; + public const float MaxNetworkEventWaitTime = 0.5f; #else - public const float MaxNetworkEventWaitTime = 0.15f; + public const float MaxNetworkEventWaitTime = 0.25f; #endif // Wait for an event to appear in the given event list (must be the very next event). From f23af57c23ea5ffb401af568d13310c59e048e7d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 11:58:11 -0500 Subject: [PATCH 44/58] Restored ClientConnected on the ServerClientId in StartClient... seems we can't rely on the server connect event to come in before messages get sent to it? --- com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 6d5027c2b7..3d9cfdb68a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -795,6 +795,7 @@ public SocketTasks StartClient() } Initialize(false); + m_MessagingSystem.ClientConnected(ServerClientId); var socketTasks = NetworkConfig.NetworkTransport.StartClient(); From 0d065be91abb07608dbe084f83128acbc432ddc2 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 14:38:29 -0500 Subject: [PATCH 45/58] Cherrypick from fast buffer reader/writer branch to fix accidentally reverted stuff. --- .../Runtime/Serialization/BitReader.cs | 9 ++- .../Runtime/Serialization/BitWriter.cs | 9 ++- .../Runtime/Serialization/BufferSerializer.cs | 33 +++++++- .../Runtime/Serialization/BytewiseUtility.cs | 80 ------------------- .../Serialization/BytewiseUtility.cs.meta | 11 --- .../Runtime/Serialization/FastBufferReader.cs | 12 +-- .../Runtime/Serialization/FastBufferWriter.cs | 12 +-- 7 files changed, 54 insertions(+), 112 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 411152046d..bb97d0c95b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Netcode { @@ -105,7 +106,7 @@ public unsafe void ReadBits(out ulong value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginReadBits)}()"); } #endif ulong val = 0; @@ -142,7 +143,7 @@ public void ReadBits(out byte value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginReadBits)}()"); } #endif value = ReadByteBits((int)bitCount); @@ -159,7 +160,7 @@ public unsafe void ReadBit(out bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginReadBits)}()"); } #endif @@ -175,7 +176,7 @@ private unsafe void ReadPartialValue(out T value, int bytesToRead, int offset var val = new T(); byte* ptr = ((byte*)&val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * k_BitsPerByte; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 688210a9f5..3895c506fe 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Netcode { @@ -116,7 +117,7 @@ public unsafe void WriteBits(ulong value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWriteBits)}()"); } #endif @@ -154,7 +155,7 @@ public void WriteBits(byte value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWriteBits)}()"); } #endif @@ -175,7 +176,7 @@ public unsafe void WriteBit(bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWriteBits)}()"); } #endif @@ -190,7 +191,7 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy { byte* ptr = ((byte*)&value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * k_BitsPerByte; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index a9c112ed8f..f6ff52e343 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -204,6 +204,9 @@ public void SerializeValue(ref T value) where T : unmanaged /// go past the point you've marked using PreCheck(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using PreCheck is to avoid bounds checking in the following /// operations in release builds. + /// + /// To get the correct size to check for, use FastBufferWriter.GetWriteSize(value) or + /// FastBufferWriter.GetWriteSize<type>() /// /// Number of bytes you plan to read or write /// True if the read/write can proceed, false otherwise. @@ -213,7 +216,11 @@ public bool PreCheck(int amount) } /// - /// Serialize a GameObject + /// Serialize a GameObject. + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref GameObject value) @@ -223,6 +230,10 @@ public void SerializeValuePreChecked(ref GameObject value) /// /// Serialize a NetworkObject + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref NetworkObject value) @@ -232,6 +243,10 @@ public void SerializeValuePreChecked(ref NetworkObject value) /// /// Serialize a NetworkBehaviour + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref NetworkBehaviour value) @@ -243,6 +258,10 @@ public void SerializeValuePreChecked(ref NetworkBehaviour value) /// Serialize a string. /// /// Note: Will ALWAYS allocate a new string when reading. + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize /// @@ -264,6 +283,10 @@ public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) /// (This is because C# doesn't allow setting an array's length value, so deserializing /// into an existing array of larger size would result in an array that doesn't have as many values /// as its Length indicates it should.) + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref T[] array) where T : unmanaged @@ -273,6 +296,10 @@ public void SerializeValuePreChecked(ref T[] array) where T : unmanaged /// /// Serialize a single byte + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref byte value) @@ -283,6 +310,10 @@ public void SerializeValuePreChecked(ref byte value) /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref T value) where T : unmanaged diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs deleted file mode 100644 index e0940c3e6a..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Netcode -{ - public static class BytewiseUtility - { - /// - /// Helper function optimized for quickly copying small numbers of bytes. - /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 - /// Slower for amount > 8 - /// - /// Pointer to the source value - /// Pointer to the destination value - /// Number of bytes to copy - public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) - { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - switch (amount) - { - case 1: - *dest = *source; - break; - case 2: - *dest = *source; - *(dest + 1) = *(source + 1); - break; - case 3: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - break; - case 4: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - break; - case 5: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - break; - case 6: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - *(dest + 5) = *(source + 5); - break; - case 7: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - *(dest + 5) = *(source + 5); - *(dest + 6) = *(source + 6); - break; - case 8: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - *(dest + 5) = *(source + 5); - *(dest + 6) = *(source + 6); - *(dest + 7) = *(source + 7); - break; - default: - UnsafeUtility.MemCpy(dest, source, amount); - break; - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta deleted file mode 100644 index 12bebf3d01..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5df078ced492f8c45966997ceda09c8f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index c0d77a6ac5..61d6456e52 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -559,7 +559,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB } if (PositionInternal + bytesToRead > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif @@ -587,7 +587,7 @@ public unsafe void ReadByte(out byte value) } if (PositionInternal + 1 > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif value = BufferPointer[PositionInternal++]; @@ -635,7 +635,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); @@ -801,13 +801,13 @@ public unsafe void ReadValue(out T value) where T : unmanaged } if (PositionInternal + len > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); + UnsafeUtility.MemCpy((byte*)ptr, BufferPointer + PositionInternal, len); } PositionInternal += len; } @@ -842,7 +842,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); + UnsafeUtility.MemCpy((byte*)ptr, BufferPointer + PositionInternal, len); } PositionInternal += len; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 5c9db03925..6de114eca6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -551,7 +551,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt } if (PositionInternal + bytesToWrite > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif @@ -577,7 +577,7 @@ public unsafe void WriteByte(byte value) } if (PositionInternal + 1 > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif BufferPointer[PositionInternal++] = value; @@ -625,7 +625,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); @@ -851,13 +851,13 @@ public unsafe void WriteValue(in T value) where T : unmanaged } if (PositionInternal + len > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); + UnsafeUtility.MemCpy(BufferPointer + PositionInternal, (byte*)ptr, len); } PositionInternal += len; } @@ -891,7 +891,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); + UnsafeUtility.MemCpy(BufferPointer + PositionInternal, (byte*)ptr, len); } PositionInternal += len; } From e8b59c9fc735a18b3af374e3e13e83fc11815df5 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 14:54:50 -0500 Subject: [PATCH 46/58] Applied much review feedback. --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 2 +- .../CodeGen/RuntimeAccessModifiersILPP.cs | 11 +- .../Runtime/Configuration/NetworkConfig.cs | 4 +- .../Runtime/Core/NetworkBehaviour.cs | 33 +- .../Runtime/Core/NetworkManager.cs | 9 +- .../Runtime/Core/NetworkObject.cs | 10 +- .../Runtime/Core/SnapshotSystem.cs | 3 +- .../Runtime/Logging/NetworkLog.cs | 1 - .../Runtime/Messaging/BatchHeader.cs | 2 +- .../Runtime/Messaging/Bind.cs | 14 - .../Runtime/Messaging/Bind.cs.meta | 11 - .../Runtime/Messaging/CustomMessageManager.cs | 1 - .../Runtime/Messaging/IMessageSender.cs | 2 +- ...essageIfSystemOwnerIsNotOfTypeAttribute.cs | 14 + ...IfSystemOwnerIsNotOfTypeAttribute.cs.meta} | 2 +- .../Messages/ChangeOwnershipMessage.cs | 2 +- .../Messages/ConnectionApprovedMessage.cs | 2 +- .../Messages/ConnectionRequestMessage.cs | 46 +- .../Messaging/Messages/CreateObjectMessage.cs | 2 +- .../Messages/DestroyObjectMessage.cs | 2 +- .../Messaging/Messages/NamedMessage.cs | 2 +- .../Messages/NetworkVariableDeltaMessage.cs | 4 +- .../Messaging/Messages/ParentSyncMessage.cs | 2 +- .../Runtime/Messaging/Messages/RpcMessage.cs | 8 +- .../Messaging/Messages/SceneEventMessage.cs | 2 +- .../Messaging/Messages/ServerLogMessage.cs | 2 +- .../Messaging/Messages/SnapshotDataMessage.cs | 2 +- .../Messaging/Messages/TimeSyncMessage.cs | 2 +- .../Messaging/Messages/UnnamedMessage.cs | 2 +- .../Runtime/Messaging/MessagingSystem.cs | 21 +- .../Runtime/Messaging/RpcParams.cs | 2 +- .../Collections/NetworkList.cs | 2 +- .../SceneManagement/NetworkSceneManager.cs | 1 - .../Runtime/SceneManagement/SceneEventData.cs | 1 - .../Runtime/Serialization/BufferSerializer.cs | 99 ---- .../Serialization/BufferSerializerReader.cs | 36 -- .../Serialization/BufferSerializerWriter.cs | 36 -- .../Runtime/Serialization/BytePacker.cs | 132 ----- .../Runtime/Serialization/ByteUnpacker.cs | 128 ----- .../Runtime/Serialization/FastBufferReader.cs | 79 --- .../FastBufferReaderExtensions.cs | 327 ------------- .../FastBufferReaderExtensions.cs.meta | 11 - .../Runtime/Serialization/FastBufferWriter.cs | 93 ---- .../FastBufferWriterExtensions.cs | 399 ---------------- .../IBufferSerializerImplementation.cs | 10 - .../Serialization/SerializationTypeTable.cs | 449 ------------------ .../SerializationTypeTable.cs.meta | 11 - .../Runtime/Spawning/NetworkSpawnManager.cs | 1 - .../Runtime/Utility/FixedUnmanagedArray.cs | 181 ------- .../Utility/FixedUnmanagedArray.cs.meta | 11 - .../Runtime/Utility/Ref.cs | 2 +- .../Editor/Messaging/MessageReceivingTests.cs | 2 +- .../Messaging/MessageRegistrationTests.cs | 8 +- .../Editor/Messaging/MessageSendingTests.cs | 2 +- .../BaseFastBufferReaderWriterTest.cs | 91 +--- .../Serialization/BufferSerializerTests.cs | 252 ---------- .../Editor/Serialization/BytePackerTests.cs | 165 ------- .../Serialization/FastBufferReaderTests.cs | 197 -------- .../Serialization/FastBufferWriterTests.cs | 143 ------ .../Assets/Tests/Runtime/RpcTestsAutomated.cs | 2 - 60 files changed, 103 insertions(+), 2990 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs rename com.unity.netcode.gameobjects/Runtime/{Serialization/FastBufferWriterExtensions.cs.meta => Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs.meta} (83%) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 60549d88d4..98f9a8ee0e 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -250,7 +250,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) } } - var networkHandlerDelegateType = typeof(NetworkManager.RpcReceive); + var networkHandlerDelegateType = typeof(NetworkManager.RpcReceiveHandler); m_NetworkHandlerDelegateCtor_MethodRef = moduleDefinition.ImportReference(networkHandlerDelegateType.GetConstructor(new[] { typeof(object), typeof(IntPtr) })); var rpcParamsType = typeof(__RpcParams); diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index 844acbb9ac..ba6d70f10e 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -88,7 +88,7 @@ private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assem fieldDefinition.IsPublic = true; } - if (fieldDefinition.Name == nameof(NetworkManager.RpcReceive)) + if (fieldDefinition.Name == nameof(NetworkManager.RpcReceiveHandler)) { fieldDefinition.IsPublic = true; } @@ -117,6 +117,15 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) fieldDefinition.IsFamily = true; } } + + foreach (var methodDefinition in typeDefinition.Methods) + { + if (methodDefinition.Name == nameof(NetworkBehaviour.SendServerRpc) + || methodDefinition.Name == nameof(NetworkBehaviour.SendClientRpc)) + { + methodDefinition.IsPublic = true; + } + } } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs index 9abaa1dd01..0a8e0b8d5b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs @@ -160,7 +160,7 @@ public class NetworkConfig public string ToBase64() { NetworkConfig config = this; - var writer = new FastBufferWriter(1300, Allocator.Temp); + var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp); using (writer) { writer.WriteValueSafe(config.ProtocolVersion); @@ -224,7 +224,7 @@ public ulong GetConfig(bool cache = true) return m_ConfigHash.Value; } - var writer = new FastBufferWriter(1300, Allocator.Temp); + var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp); using (writer) { writer.WriteValueSafe(ProtocolVersion); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 76ec621d2f..20b1d692d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Linq; using Unity.Collections; -using Unity.Netcode.Messages; namespace Unity.Netcode { @@ -37,7 +36,7 @@ internal enum __RpcExecStage #pragma warning restore IDE1006 // restore naming rule violation check - public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams sendParams, RpcDelivery delivery) + internal void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams rpcParams, RpcDelivery delivery) { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; switch (delivery) @@ -46,7 +45,7 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: - if (writer.Length > 1300 - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)) + if (writer.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)) { throw new OverflowException("RPC parameters are too large for unreliable delivery."); } @@ -63,7 +62,7 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR NetworkBehaviourId = NetworkBehaviourId, NetworkMethodId = rpcMethodId }, - RPCData = writer + RpcData = writer }; var rpcMessageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ServerClientId, true); #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -79,7 +78,7 @@ public void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerR #endif } - public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, ClientRpcParams sendParams, RpcDelivery delivery) + internal unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, ClientRpcParams rpcParams, RpcDelivery delivery) { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; switch (delivery) @@ -88,7 +87,7 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, networkDelivery = NetworkDelivery.ReliableFragmentedSequenced; break; case RpcDelivery.Unreliable: - if (writer.Length > 1300 - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)) + if (writer.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)) { throw new OverflowException("RPC parameters are too large for unreliable delivery."); } @@ -105,26 +104,24 @@ public unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, NetworkBehaviourId = NetworkBehaviourId, NetworkMethodId = rpcMethodId }, - RPCData = writer + RpcData = writer }; int messageSize; - if (sendParams.Send.TargetClientIds != null) + if (rpcParams.Send.TargetClientIds != null) { // Copy into a localArray because SendMessage doesn't take IEnumerable, only IReadOnlyList - ulong* localArray = stackalloc ulong[sendParams.Send.TargetClientIds.Count()]; - var idx = 0; - foreach (var clientId in sendParams.Send.TargetClientIds) + ulong* localArray = stackalloc ulong[rpcParams.Send.TargetClientIds.Count()]; + var index = 0; + foreach (var clientId in rpcParams.Send.TargetClientIds) { - localArray[idx++] = clientId; + localArray[index++] = clientId; } - messageSize = NetworkManager.SendMessage(message, networkDelivery, localArray, idx, true); + messageSize = NetworkManager.SendMessage(message, networkDelivery, localArray, index, true); } - else if (sendParams.Send.TargetClientIdsNativeArray != null) + else if (rpcParams.Send.TargetClientIdsNativeArray != null) { - // NativeArray doesn't implement required IReadOnlyList interface, but that's ok, pointer + length - // will be more efficient anyway. - messageSize = NetworkManager.SendMessage(message, networkDelivery, sendParams.Send.TargetClientIdsNativeArray.Value); + messageSize = NetworkManager.SendMessage(message, networkDelivery, rpcParams.Send.TargetClientIdsNativeArray.Value); } else { @@ -453,7 +450,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) // so we don't have to do this serialization work if we're not going to use the result. if (IsServer && clientId == NetworkManager.ServerClientId) { - var tmpWriter = new FastBufferWriter(1300, Allocator.Temp); + var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp); #pragma warning disable CS0728 // Warns that tmpWriter may be reassigned within Serialize, but Serialize does not reassign it. using (tmpWriter) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 3d9cfdb68a..fc82cca21e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -4,7 +4,6 @@ using System.Linq; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode.Messages; using UnityEngine; #if UNITY_EDITOR using UnityEditor; @@ -27,10 +26,10 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem #pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `public` - internal delegate void RpcReceive(NetworkBehaviour behaviour, ref FastBufferReader reader, __RpcParams parameters); + internal delegate void RpcReceiveHandler(NetworkBehaviour behaviour, ref FastBufferReader reader, __RpcParams parameters); // RuntimeAccessModifiersILPP will make this `public` - internal static readonly Dictionary __rpc_func_table = new Dictionary(); + internal static readonly Dictionary __rpc_func_table = new Dictionary(); #if DEVELOPMENT_BUILD || UNITY_EDITOR // RuntimeAccessModifiersILPP will make this `public` @@ -1143,9 +1142,7 @@ private void SendConnectionRequest() { ConfigHash = NetworkConfig.GetConfig(), ShouldSendConnectionData = NetworkConfig.ConnectionApproval, - ConnectionData = - new FixedUnmanagedArray(NetworkConfig - .ConnectionData) + ConnectionData = NetworkConfig.ConnectionData }; SendMessage(message, NetworkDelivery.ReliableSequenced, ServerClientId); } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index dfff0a267f..c3d4870aba 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using Unity.Netcode.Messages; using UnityEngine; namespace Unity.Netcode @@ -11,7 +10,7 @@ namespace Unity.Netcode /// [AddComponentMenu("Netcode/" + nameof(NetworkObject), -99)] [DisallowMultipleComponent] - public sealed class NetworkObject : MonoBehaviour, INetworkSerializable + public sealed class NetworkObject : MonoBehaviour { [HideInInspector] [SerializeField] @@ -60,7 +59,7 @@ internal void GenerateGlobalObjectIdHash() /// /// Gets the unique Id of this object that is synced across the network /// - public ulong NetworkObjectId { get => m_NetworkObjectId; internal set => m_NetworkObjectId = value; } + public ulong NetworkObjectId { get; internal set; } /// /// Gets the ClientId of the owner of this NetworkObject @@ -1143,10 +1142,5 @@ internal uint HostCheckForGlobalObjectIdHashOverride() return GlobalObjectIdHash; } - - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation - { - serializer.SerializeValue(ref m_NetworkObjectId); - } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index 38c712fe4a..740f285115 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode.Messages; using UnityEngine; namespace Unity.Netcode @@ -941,7 +940,7 @@ internal void Store(ulong networkObjectId, int behaviourIndex, int variableIndex private unsafe void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBase networkVariable, int index) { // write var into buffer, possibly adjusting entry's position and Length - var varBuffer = new FastBufferWriter(1300, Allocator.Temp); + var varBuffer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp); #pragma warning disable CS0728 // Warns that varBuffer may be reassigned within ReadDelta, but ReadDelta does not reassign it. using (varBuffer) { diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index a6149a494a..68ee6f895f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -1,4 +1,3 @@ -using Unity.Netcode.Messages; using UnityEngine; namespace Unity.Netcode diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs index 68afbe2651..a423a77764 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/BatchHeader.cs @@ -1,6 +1,6 @@ namespace Unity.Netcode { - public struct BatchHeader + internal struct BatchHeader { public ushort BatchSize; } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs deleted file mode 100644 index f5bb5e1364..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Unity.Netcode -{ - public class Bind : Attribute - { - public Type BoundType; - - public Bind(Type boundType) - { - BoundType = boundType; - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta deleted file mode 100644 index 76e8e8e1a8..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Bind.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9697b6526afcb8a429e65b78d4453e90 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index 6aa0a20d86..8b2dacc042 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Unity.Netcode.Messages; namespace Unity.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs index c265b29f1d..0b482f16c9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IMessageSender.cs @@ -1,6 +1,6 @@ namespace Unity.Netcode { - public interface IMessageSender + internal interface IMessageSender { void Send(ulong clientId, NetworkDelivery delivery, ref FastBufferWriter batchData); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs new file mode 100644 index 0000000000..153954c214 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Unity.Netcode +{ + internal class IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute : Attribute + { + public Type BoundType; + + public IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute(Type boundType) + { + BoundType = boundType; + } + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs.meta index b96c1f3b21..59e5c56fff 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1a9dd964797964b4a99e03706516a8a9 +guid: ce0f65a9064001941876d49ba1751050 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs index 553aa29c54..066da1a91e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct ChangeOwnershipMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs index 2a10ec35f6..a54ef48e3d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct ConnectionApprovedMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs index 1e3a2f67a3..83a829a108 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs @@ -1,19 +1,10 @@ -using System; -using System.Runtime.InteropServices; - -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct ConnectionRequestMessage : INetworkMessage { public ulong ConfigHash; - [StructLayout(LayoutKind.Explicit, Size = 512)] - public struct ConnectionDataStorage : IFixedArrayStorage - { - - } - - public FixedUnmanagedArray ConnectionData; + public byte[] ConnectionData; public bool ShouldSendConnectionData; @@ -21,24 +12,12 @@ public void Serialize(ref FastBufferWriter writer) { if (ShouldSendConnectionData) { - if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash) + - FastBufferWriter.GetWriteSize(ConnectionData))) - { - throw new OverflowException( - $"Not enough space in the write buffer to serialize {nameof(ConnectionRequestMessage)}"); - } - writer.WriteValue(ConfigHash); - writer.WriteValue(ConnectionData.Count); - writer.WriteValue(ConnectionData, ConnectionData.Count); + writer.WriteValueSafe(ConfigHash); + writer.WriteValueSafe(ConnectionData); } else { - if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(ConfigHash))) - { - throw new OverflowException( - $"Not enough space in the write buffer to serialize {nameof(ConnectionRequestMessage)}"); - } - writer.WriteValue(ConfigHash); + writer.WriteValueSafe(ConfigHash); } } @@ -77,18 +56,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) return; } - reader.ReadValue(out int length); - if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize() * length)) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Incomplete connection request message."); - } - - networkManager.DisconnectClient(context.SenderId); - return; - } - reader.ReadValue(out message.ConnectionData, length); + reader.ReadValueSafe(out message.ConnectionData); } else { @@ -130,7 +98,7 @@ public void Handle(NetworkManager networkManager, ulong senderId) { // Note: Delegate creation allocates. // Note: ToArray() also allocates. :( - networkManager.InvokeConnectionApproval(ConnectionData.ToArray(), senderId, + networkManager.InvokeConnectionApproval(ConnectionData, senderId, (createPlayerObject, playerPrefabHash, approved, position, rotation) => { var localCreatePlayerObject = createPlayerObject; diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs index 83a7cae252..ac58ff3f69 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct CreateObjectMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs index a6ecbb3175..03adaecdbb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct DestroyObjectMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs index d50ac193f3..6dc01306da 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NamedMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct NamedMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs index f548b218fa..2c659b7522 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Unity.Collections; -namespace Unity.Netcode.Messages +namespace Unity.Netcode { /// /// This particular struct is a little weird because it doesn't actually contain the data @@ -66,7 +66,7 @@ public void Serialize(ref FastBufferWriter writer) { if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - var tmpWriter = new FastBufferWriter(1300, Allocator.Temp, short.MaxValue); + var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue); NetworkBehaviour.NetworkVariableFields[k].WriteDelta(ref tmpWriter); writer.WriteValueSafe((ushort)tmpWriter.Length); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs index 8ba0bb53ef..fdee00dc80 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ParentSyncMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { public struct ParentSyncMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs index 848ae021c4..9cae59c338 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessage.cs @@ -1,6 +1,6 @@ using System; -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct RpcMessage : INetworkMessage { @@ -19,17 +19,17 @@ public struct HeaderData } public HeaderData Header; - public FastBufferWriter RPCData; + public FastBufferWriter RpcData; public unsafe void Serialize(ref FastBufferWriter writer) { - if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Header) + RPCData.Length)) + if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Header) + RpcData.Length)) { throw new OverflowException("Not enough space in the buffer to store RPC data."); } writer.WriteValue(Header); - writer.WriteBytes(RPCData.GetUnsafePtr(), RPCData.Length); + writer.WriteBytes(RpcData.GetUnsafePtr(), RpcData.Length); } public static void Receive(ref FastBufferReader reader, NetworkContext context) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs index 2b2632f624..0b4c5e1650 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { // Todo: Would be lovely to get this one nicely formatted with all the data it sends in the struct // like most of the other messages when we have some more time and can come back and refactor this. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs index 66a21cc497..3dddc93d98 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct ServerLogMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs index 797e5e75db..d1d84d49e4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs @@ -2,7 +2,7 @@ using Unity.Collections.LowLevel.Unsafe; using UnityEngine; -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct SnapshotDataMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs index 380858391b..0bdd1389f5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/TimeSyncMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct TimeSyncMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs index 7fb49e5676..c1df7fe707 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.Messages +namespace Unity.Netcode { internal struct UnnamedMessage : INetworkMessage { diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index 1830632ec3..799bee2f8d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -9,13 +9,13 @@ namespace Unity.Netcode { - public class InvalidMessageStructureException : SystemException + internal class InvalidMessageStructureException : SystemException { public InvalidMessageStructureException() { } public InvalidMessageStructureException(string issue) : base(issue) { } } - public class MessagingSystem : IDisposable + internal class MessagingSystem : IDisposable { private struct ReceiveQueueItem { @@ -66,6 +66,9 @@ internal byte GetMessageType(Type t) return m_MessageTypes[t]; } + public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300; + public const int FRAGMENTED_MESSAGE_MAX_SIZE = 64000; + public MessagingSystem(IMessageSender messageSender, object owner, ulong localClientId = long.MaxValue) { try @@ -87,7 +90,7 @@ public MessagingSystem(IMessageSender messageSender, object owner, ulong localCl if (interfaceType.IsAssignableFrom(type)) { - var attributes = type.GetCustomAttributes(typeof(Bind), false); + var attributes = type.GetCustomAttributes(typeof(IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute), false); // If [Bind(ownerType)] isn't provided, it defaults to being bound to NetworkManager // This is technically a breach of domain by having MessagingSystem know about the existence // of NetworkManager... but ultimately, Bind is provided to support testing, not to support @@ -99,7 +102,7 @@ public MessagingSystem(IMessageSender messageSender, object owner, ulong localCl var allowedToBind = attributes.Length == 0 && m_Owner is NetworkManager; for (var i = 0; i < attributes.Length; ++i) { - var bindAttribute = (Bind)attributes[i]; + var bindAttribute = (IgnoreMessageIfSystemOwnerIsNotOfTypeAttribute)attributes[i]; if ( (bindAttribute.BoundType != null && bindAttribute.BoundType.IsInstanceOfType(m_Owner)) || @@ -299,7 +302,7 @@ public void HandleMessage(in MessageHeader header, ref FastBufferReader reader, } catch (Exception e) { - Debug.LogError(e); + Debug.LogException(e); } } #pragma warning restore CS0728 // Warns that reader may be reassigned within the handler, but the handler does not reassign it. @@ -368,8 +371,8 @@ internal unsafe int SendMessage(in TMessageType where TMessageType : INetworkMessage where TClientIdListType : IReadOnlyList { - var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? 64000 : 1300; - var tmpSerializer = new FastBufferWriter(1300, Allocator.Temp, maxSize); + var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE; + var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, maxSize); #pragma warning disable CS0728 // Warns that tmpSerializer may be reassigned within Serialize, but Serialize does not reassign it. using (tmpSerializer) { @@ -392,7 +395,7 @@ internal unsafe int SendMessage(in TMessageType var sendQueueItem = m_SendQueues[clientId]; if (sendQueueItem.Length == 0) { - sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, + sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob, maxSize)); sendQueueItem.GetUnsafeList()->ElementAt(0).Writer.Seek(sizeof(BatchHeader)); } @@ -402,7 +405,7 @@ internal unsafe int SendMessage(in TMessageType if (lastQueueItem.NetworkDelivery != delivery || lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position < tmpSerializer.Length) { - sendQueueItem.Add(new SendQueueItem(delivery, 1300, Allocator.TempJob, + sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob, maxSize)); sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader)); } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs index 76880b26ba..a8549aab1d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs @@ -22,7 +22,7 @@ public struct ClientRpcSendParams { /// /// IEnumerable version of target id list - use either this OR TargetClientIdsNativeArray - /// Note: Even if you provide a value such as NativeArray, enumerating it will cause boxing. + /// Note: Even if you provide a value type such as NativeArray, enumerating it will cause boxing. /// If you want to avoid boxing, use TargetClientIdsNativeArray /// public IEnumerable TargetClientIds; diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index 8803e669eb..2d3582f028 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -81,7 +81,7 @@ public override void WriteDelta(ref FastBufferWriter writer) return; } - + writer.WriteValueSafe((ushort)m_DirtyEvents.Length); for (int i = 0; i < m_DirtyEvents.Length; i++) { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index b941bdad31..d4557fa9f8 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System; using System.Linq; -using Unity.Netcode.Messages; using UnityEngine; using UnityEngine.SceneManagement; diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 2d9dc9b1ac..f371eb942b 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using Unity.Collections; -using Unity.Netcode.Messages; using UnityEngine.SceneManagement; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index f6ff52e343..5d2a37b13c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -1,6 +1,3 @@ -using System; -using UnityEngine; - namespace Unity.Netcode { /// @@ -61,27 +58,6 @@ public ref FastBufferWriter GetFastBufferWriter() return ref m_Implementation.GetFastBufferWriter(); } - /// - /// Serialize an object value. - /// Note: Will ALWAYS cause allocations when reading. - /// This function is also much slower than the others as it has to figure out how to serialize - /// the object using runtime reflection. - /// It's recommended not to use this unless you have no choice. - /// - /// Throws OverflowException if the end of the buffer has been reached. - /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. - /// - /// Value to serialize - /// Type to deserialize to when reading - /// - /// If true, will force an isNull byte to be written. - /// Some types will write this byte regardless. - /// - public void SerializeValue(ref object value, Type type, bool isNullable = false) - { - m_Implementation.SerializeValue(ref value, type, isNullable); - } - /// /// Serialize an INetworkSerializable /// @@ -94,42 +70,6 @@ public void SerializeValue(ref object value, Type type, bool isNullable = false) m_Implementation.SerializeNetworkSerializable(ref value); } - /// - /// Serialize a GameObject - /// - /// Throws OverflowException if the end of the buffer has been reached. - /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. - /// - /// Value to serialize - public void SerializeValue(ref GameObject value) - { - m_Implementation.SerializeValue(ref value); - } - - /// - /// Serialize a NetworkObject - /// - /// Throws OverflowException if the end of the buffer has been reached. - /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. - /// - /// Value to serialize - public void SerializeValue(ref NetworkObject value) - { - m_Implementation.SerializeValue(ref value); - } - - /// - /// Serialize a NetworkBehaviour - /// - /// Throws OverflowException if the end of the buffer has been reached. - /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. - /// - /// Value to serialize - public void SerializeValue(ref NetworkBehaviour value) - { - m_Implementation.SerializeValue(ref value); - } - /// /// Serialize a string. /// @@ -215,45 +155,6 @@ public bool PreCheck(int amount) return m_Implementation.PreCheck(amount); } - /// - /// Serialize a GameObject. - /// - /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only - /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple - /// serialization operations in one function call instead of having to do bounds checking on every call. - /// - /// Value to serialize - public void SerializeValuePreChecked(ref GameObject value) - { - m_Implementation.SerializeValuePreChecked(ref value); - } - - /// - /// Serialize a NetworkObject - /// - /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only - /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple - /// serialization operations in one function call instead of having to do bounds checking on every call. - /// - /// Value to serialize - public void SerializeValuePreChecked(ref NetworkObject value) - { - m_Implementation.SerializeValuePreChecked(ref value); - } - - /// - /// Serialize a NetworkBehaviour - /// - /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only - /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple - /// serialization operations in one function call instead of having to do bounds checking on every call. - /// - /// Value to serialize - public void SerializeValuePreChecked(ref NetworkBehaviour value) - { - m_Implementation.SerializeValuePreChecked(ref value); - } - /// /// Serialize a string. /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index ec64ed89ac..b7633d11b6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -1,5 +1,4 @@ using System; -using UnityEngine; namespace Unity.Netcode { @@ -25,26 +24,6 @@ public ref FastBufferWriter GetFastBufferWriter() throw new InvalidOperationException("Cannot retrieve a FastBufferWriter from a serializer where IsWriter = false"); } - public void SerializeValue(ref object value, Type type, bool isNullable = false) - { - m_Reader.Value.ReadObject(out value, type, isNullable); - } - - public void SerializeValue(ref GameObject value) - { - m_Reader.Value.ReadValueSafe(out value); - } - - public void SerializeValue(ref NetworkObject value) - { - m_Reader.Value.ReadValueSafe(out value); - } - - public void SerializeValue(ref NetworkBehaviour value) - { - m_Reader.Value.ReadValueSafe(out value); - } - public void SerializeValue(ref string s, bool oneByteChars = false) { m_Reader.Value.ReadValueSafe(out s, oneByteChars); @@ -75,21 +54,6 @@ public bool PreCheck(int amount) return m_Reader.Value.TryBeginRead(amount); } - public void SerializeValuePreChecked(ref GameObject value) - { - m_Reader.Value.ReadValue(out value); - } - - public void SerializeValuePreChecked(ref NetworkObject value) - { - m_Reader.Value.ReadValue(out value); - } - - public void SerializeValuePreChecked(ref NetworkBehaviour value) - { - m_Reader.Value.ReadValue(out value); - } - public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) { m_Reader.Value.ReadValue(out s, oneByteChars); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index e9ddd01dc0..60569225f6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -1,5 +1,4 @@ using System; -using UnityEngine; namespace Unity.Netcode { @@ -25,26 +24,6 @@ public ref FastBufferWriter GetFastBufferWriter() return ref m_Writer.Value; } - public void SerializeValue(ref object value, Type type, bool isNullable = false) - { - m_Writer.Value.WriteObject(value, isNullable); - } - - public void SerializeValue(ref GameObject value) - { - m_Writer.Value.WriteValueSafe(value); - } - - public void SerializeValue(ref NetworkObject value) - { - m_Writer.Value.WriteValueSafe(value); - } - - public void SerializeValue(ref NetworkBehaviour value) - { - m_Writer.Value.WriteValueSafe(value); - } - public void SerializeValue(ref string s, bool oneByteChars = false) { m_Writer.Value.WriteValueSafe(s, oneByteChars); @@ -75,21 +54,6 @@ public bool PreCheck(int amount) return m_Writer.Value.TryBeginWrite(amount); } - public void SerializeValuePreChecked(ref GameObject value) - { - m_Writer.Value.WriteValue(value); - } - - public void SerializeValuePreChecked(ref NetworkObject value) - { - m_Writer.Value.WriteValue(value); - } - - public void SerializeValuePreChecked(ref NetworkBehaviour value) - { - m_Writer.Value.WriteValue(value); - } - public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) { m_Writer.Value.WriteValue(s, oneByteChars); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 685cc911d9..d7ec66ef4f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -9,138 +9,6 @@ namespace Unity.Netcode /// public static class BytePacker { - - /// - /// Writes a boxed object in a packed format - /// Named differently from other WriteValuePacked methods to avoid accidental boxing. - /// Don't use this method unless you have no other choice. - /// - /// Writer to write to - /// The object to write - /// - /// If true, an extra byte will be written to indicate whether or not the value is null. - /// Some types will always write this. - /// - public static void WriteObjectPacked(ref FastBufferWriter writer, object value, bool isNullable = false) - { -#if UNITY_NETCODE_DEBUG_NO_PACKING - writer.WriteObject(value, isNullable); - return; -#endif - if (isNullable || value.GetType().IsNullable()) - { - bool isNull = value == null || (value is UnityEngine.Object o && o == null); - - WriteValuePacked(ref writer, isNull); - - if (isNull) - { - return; - } - } - - var type = value.GetType(); - var hasSerializer = SerializationTypeTable.SerializersPacked.TryGetValue(type, out var serializer); - if (hasSerializer) - { - serializer(ref writer, value); - return; - } - - if (value is Array array) - { - WriteValuePacked(ref writer, array.Length); - - for (int i = 0; i < array.Length; i++) - { - WriteObjectPacked(ref writer, array.GetValue(i)); - } - } - - if (value.GetType().IsEnum) - { - switch (Convert.GetTypeCode(value)) - { - case TypeCode.Boolean: - WriteValuePacked(ref writer, (byte)value); - break; - case TypeCode.Char: - WriteValuePacked(ref writer, (char)value); - break; - case TypeCode.SByte: - WriteValuePacked(ref writer, (sbyte)value); - break; - case TypeCode.Byte: - WriteValuePacked(ref writer, (byte)value); - break; - case TypeCode.Int16: - WriteValuePacked(ref writer, (short)value); - break; - case TypeCode.UInt16: - WriteValuePacked(ref writer, (ushort)value); - break; - case TypeCode.Int32: - WriteValuePacked(ref writer, (int)value); - break; - case TypeCode.UInt32: - WriteValuePacked(ref writer, (uint)value); - break; - case TypeCode.Int64: - WriteValuePacked(ref writer, (long)value); - break; - case TypeCode.UInt64: - WriteValuePacked(ref writer, (ulong)value); - break; - } - return; - } - if (value is GameObject) - { - ((GameObject)value).TryGetComponent(out var networkObject); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - WriteValuePacked(ref writer, networkObject.NetworkObjectId); - return; - } - if (value is NetworkObject) - { - if (!((NetworkObject)value).IsSpawned) - { - throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); - } - - WriteValuePacked(ref writer, ((NetworkObject)value).NetworkObjectId); - return; - } - if (value is NetworkBehaviour) - { - if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(BytePacker)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); - } - - WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkObjectId); - WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkBehaviourId); - return; - } - if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - } - - throw new ArgumentException($"{nameof(BytePacker)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index 98ed8c9320..a6fee91f47 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -7,134 +7,6 @@ namespace Unity.Netcode public static class ByteUnpacker { - /// - /// Reads a boxed object in a packed format - /// Named differently from other ReadValuePacked methods to avoid accidental boxing - /// Don't use this method unless you have no other choice. - /// - /// The reader to read from - /// The object to read - /// The type of the object to read (i.e., typeof(int)) - /// - /// If true, reads a byte indicating whether or not the object is null. - /// Should match the way the object was written. - /// - public static void ReadObjectPacked(ref FastBufferReader reader, out object value, Type type, bool isNullable = false) - { -#if UNITY_NETCODE_DEBUG_NO_PACKING - reader.ReadObject(out value, type, isNullable); - return; -#endif - if (isNullable || type.IsNullable()) - { - reader.ReadValueSafe(out bool isNull); - - if (isNull) - { - value = null; - return; - } - } - - var hasDeserializer = SerializationTypeTable.DeserializersPacked.TryGetValue(type, out var deserializer); - if (hasDeserializer) - { - deserializer(ref reader, out value); - return; - } - - if (type.IsArray && type.HasElementType) - { - ReadValuePacked(ref reader, out int length); - - var arr = Array.CreateInstance(type.GetElementType(), length); - - for (int i = 0; i < length; i++) - { - ReadObjectPacked(ref reader, out object item, type.GetElementType()); - arr.SetValue(item, i); - } - - value = arr; - return; - } - - if (type.IsEnum) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Boolean: - ReadValuePacked(ref reader, out byte boolVal); - value = Enum.ToObject(type, boolVal != 0); - return; - case TypeCode.Char: - ReadValuePacked(ref reader, out char charVal); - value = Enum.ToObject(type, charVal); - return; - case TypeCode.SByte: - ReadValuePacked(ref reader, out byte sbyteVal); - value = Enum.ToObject(type, sbyteVal); - return; - case TypeCode.Byte: - ReadValuePacked(ref reader, out byte byteVal); - value = Enum.ToObject(type, byteVal); - return; - case TypeCode.Int16: - ReadValuePacked(ref reader, out short shortVal); - value = Enum.ToObject(type, shortVal); - return; - case TypeCode.UInt16: - ReadValuePacked(ref reader, out ushort ushortVal); - value = Enum.ToObject(type, ushortVal); - return; - case TypeCode.Int32: - ReadValuePacked(ref reader, out int intVal); - value = Enum.ToObject(type, intVal); - return; - case TypeCode.UInt32: - ReadValuePacked(ref reader, out uint uintVal); - value = Enum.ToObject(type, uintVal); - return; - case TypeCode.Int64: - ReadValuePacked(ref reader, out long longVal); - value = Enum.ToObject(type, longVal); - return; - case TypeCode.UInt64: - ReadValuePacked(ref reader, out ulong ulongVal); - value = Enum.ToObject(type, ulongVal); - return; - } - } - - if (type == typeof(GameObject)) - { - reader.ReadValueSafe(out GameObject go); - value = go; - return; - } - - if (type == typeof(NetworkObject)) - { - reader.ReadValueSafe(out NetworkObject no); - value = no; - return; - } - - if (typeof(NetworkBehaviour).IsAssignableFrom(type)) - { - reader.ReadValueSafe(out NetworkBehaviour nb); - value = nb; - return; - } - /*if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - }*/ - - throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); - } - #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 61d6456e52..73d58e4587 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -703,85 +703,6 @@ public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) } } - /// - /// Read a value of type FixedUnmanagedArray from the buffer. - /// - /// The value to copy - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValue(out FixedUnmanagedArray value, int count) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - int len = sizeof(TPropertyType) * count; -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferWriter in bytewise mode while in a bitwise context."); - } - if (PositionInternal + len > AllowedReadMark) - { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); - } -#endif - - value = new FixedUnmanagedArray(); - BytewiseUtility.FastCopyBytes((byte*)value.GetArrayPtr(), BufferPointer + PositionInternal, len); - value.Count = len; - PositionInternal += len; - } - - /// - /// Read a value of type FixedUnmanagedArray from the buffer. - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling TryBeginRead. - /// - /// The value to copy - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValueSafe(out FixedUnmanagedArray value, int count) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - int len = sizeof(TPropertyType) * count; -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferWriter in bytewise mode while in a bitwise context."); - } - if (PositionInternal + len > AllowedReadMark) - { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); - } -#endif - - value = new FixedUnmanagedArray(); - BytewiseUtility.FastCopyBytes((byte*)value.GetArrayPtr(), BufferPointer + PositionInternal, len); - value.Count = len; - PositionInternal += len; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("FixedUnmanagedArray must be written/read using a count.")] - public void ReadValue(out FixedUnmanagedArray value) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("FixedUnmanagedArray must be written/read using a count.")] - public void ReadValueSafe(out FixedUnmanagedArray value) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); - } - /// /// Read a value of any unmanaged type to the buffer. /// It will be copied from the buffer exactly as it existed in memory on the writing end. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs deleted file mode 100644 index 0e6a668c5e..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ /dev/null @@ -1,327 +0,0 @@ - -using System; -using UnityEngine; - -namespace Unity.Netcode -{ - public static class FastBufferReaderExtensions - { - - /// - /// Reads a boxed object in a standard format - /// Named differently from other ReadValue methods to avoid accidental boxing - /// - /// The reader to use to read the value - /// The object to read - /// The type to be read - /// - /// If true, reads a byte indicating whether or not the object is null. - /// Should match the way the object was written. - /// - public static void ReadObject(this ref FastBufferReader reader, out object value, Type type, bool isNullable = false) - { - if (isNullable || type.IsNullable()) - { - reader.ReadValueSafe(out bool isNull); - - if (isNull) - { - value = null; - return; - } - } - - var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); - if (hasDeserializer) - { - deserializer(ref reader, out value); - return; - } - - if (type.IsArray && type.HasElementType) - { - reader.ReadValueSafe(out int length); - - var arr = Array.CreateInstance(type.GetElementType(), length); - - for (int i = 0; i < length; i++) - { - reader.ReadObject(out object item, type.GetElementType()); - arr.SetValue(item, i); - } - - value = arr; - return; - } - - if (type.IsEnum) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Boolean: - reader.ReadValueSafe(out byte boolVal); - value = Enum.ToObject(type, boolVal != 0); - return; - case TypeCode.Char: - reader.ReadValueSafe(out char charVal); - value = Enum.ToObject(type, charVal); - return; - case TypeCode.SByte: - reader.ReadValueSafe(out sbyte sbyteVal); - value = Enum.ToObject(type, sbyteVal); - return; - case TypeCode.Byte: - reader.ReadValueSafe(out byte byteVal); - value = Enum.ToObject(type, byteVal); - return; - case TypeCode.Int16: - reader.ReadValueSafe(out short shortVal); - value = Enum.ToObject(type, shortVal); - return; - case TypeCode.UInt16: - reader.ReadValueSafe(out ushort ushortVal); - value = Enum.ToObject(type, ushortVal); - return; - case TypeCode.Int32: - reader.ReadValueSafe(out int intVal); - value = Enum.ToObject(type, intVal); - return; - case TypeCode.UInt32: - reader.ReadValueSafe(out uint uintVal); - value = Enum.ToObject(type, uintVal); - return; - case TypeCode.Int64: - reader.ReadValueSafe(out long longVal); - value = Enum.ToObject(type, longVal); - return; - case TypeCode.UInt64: - reader.ReadValueSafe(out ulong ulongVal); - value = Enum.ToObject(type, ulongVal); - return; - } - } - - if (type == typeof(GameObject)) - { - reader.ReadValueSafe(out GameObject go); - value = go; - return; - } - - if (type == typeof(NetworkObject)) - { - reader.ReadValueSafe(out NetworkObject no); - value = no; - return; - } - - if (typeof(NetworkBehaviour).IsAssignableFrom(type)) - { - reader.ReadValueSafe(out NetworkBehaviour nb); - value = nb; - return; - } - /*if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - }*/ - - throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Read a GameObject - /// - /// The reader to use to read the value - /// value to read - public static void ReadValue(this ref FastBufferReader reader, out GameObject value) - { - reader.ReadValue(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.gameObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a GameObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling TryBeginRead. - /// - /// The reader to use to read the value - /// value to read - public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject value) - { - reader.ReadValueSafe(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.gameObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read an array of GameObjects - /// - /// The reader to use to read the value - /// value to read - public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject[] value) - { - reader.ReadValueSafe(out int size); - value = new GameObject[size]; - for (var i = 0; i < size; ++i) - { - reader.ReadValueSafe(out value[i]); - } - } - - /// - /// Read a NetworkObject - /// - /// The reader to use to read the value - /// value to read - public static void ReadValue(this ref FastBufferReader reader, out NetworkObject value) - { - reader.ReadValue(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling TryBeginRead. - /// - /// The reader to use to read the value - /// value to read - public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject value) - { - reader.ReadValueSafe(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read an array of NetworkObjects - /// - /// The reader to use to read the value - /// value to read - public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject[] value) - { - reader.ReadValueSafe(out int size); - value = new NetworkObject[size]; - for (var i = 0; i < size; ++i) - { - reader.ReadValueSafe(out value[i]); - } - } - - /// - /// Read a NetworkBehaviour - /// - /// The reader to use to read the value - /// value to read - public static void ReadValue(this ref FastBufferReader reader, out NetworkBehaviour value) - { - reader.ReadValue(out ulong networkObjectId); - reader.ReadValue(out ushort networkBehaviourId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkBehaviour - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling TryBeginRead. - /// - /// The reader to use to read the value - /// value to read - public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour value) - { - reader.ReadValueSafe(out ulong networkObjectId); - reader.ReadValueSafe(out ushort networkBehaviourId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read an array of NetworkBehaviours - /// - /// The reader to use to read the value - /// value to read - public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour[] value) - { - reader.ReadValueSafe(out int size); - value = new NetworkBehaviour[size]; - for (var i = 0; i < size; ++i) - { - reader.ReadValueSafe(out value[i]); - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta deleted file mode 100644 index d00798303c..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4dc2c9158967ec847895c0d9653283fa -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 6de114eca6..f4e8b78159 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -717,99 +717,6 @@ public unsafe void CopyFrom(ref FastBufferWriter other) WriteBytes(other.BufferPointer, other.PositionInternal); } - - /// - /// Get the size required to write a FixedUnmanagedArray - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(in FixedUnmanagedArray value, int count) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - return count * sizeof(TPropertyType); - } - - /// - /// Write a value of type FixedUnmanagedArray to the buffer. - /// - /// The value to copy - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValue(in FixedUnmanagedArray value, int count) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - int len = sizeof(TPropertyType) * count; - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferWriter in bytewise mode while in a bitwise context."); - } - if (PositionInternal + len > AllowedWriteMark) - { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); - } -#endif - - BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)value.GetArrayPtr(), len); - PositionInternal += len; - } - - /// - /// Write a value of type FixedUnmanagedArray to the buffer. - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The value to copy - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValueSafe(in FixedUnmanagedArray value, int count) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - int len = sizeof(TPropertyType) * count; - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferWriter in bytewise mode while in a bitwise context."); - } -#endif - - if (!TryBeginWriteInternal(len)) - { - throw new OverflowException("Writing past the end of the buffer"); - } - - BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)value.GetArrayPtr(), len); - PositionInternal += len; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("FixedUnmanagedArray must be written/read using a count.")] - public void WriteValue(in FixedUnmanagedArray value) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("FixedUnmanagedArray must be written/read using a count.")] - public void WriteValueSafe(in FixedUnmanagedArray value) - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - throw new NotSupportedException("FixedUnmanagedArray must be written/read using a count."); - } - /// /// Get the size required to write an unmanaged value /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs deleted file mode 100644 index 2d56adb036..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ /dev/null @@ -1,399 +0,0 @@ - -using System; -using UnityEngine; - -namespace Unity.Netcode -{ - public static class FastBufferWriterExtensions - { - - /// - /// Writes a boxed object in a standard format - /// Named differently from other WriteValue methods to avoid accidental boxing - /// - /// The writer to use to write the value - /// The object to write - /// - /// If true, an extra byte will be written to indicate whether or not the value is null. - /// Some types will always write this. - /// - public static void WriteObject(this ref FastBufferWriter writer, object value, bool isNullable = false) - { - if (isNullable || value.GetType().IsNullable()) - { - bool isNull = value == null || (value is UnityEngine.Object o && o == null); - - writer.WriteValueSafe(isNull); - - if (isNull) - { - return; - } - } - - var type = value.GetType(); - var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); - if (hasSerializer) - { - serializer(ref writer, value); - return; - } - - if (value is Array array) - { - writer.WriteValueSafe(array.Length); - - for (int i = 0; i < array.Length; i++) - { - writer.WriteObject(array.GetValue(i)); - } - - return; - } - - if (value.GetType().IsEnum) - { - switch (Convert.GetTypeCode(value)) - { - case TypeCode.Boolean: - writer.WriteValueSafe((byte)value); - break; - case TypeCode.Char: - writer.WriteValueSafe((char)value); - break; - case TypeCode.SByte: - writer.WriteValueSafe((sbyte)value); - break; - case TypeCode.Byte: - writer.WriteValueSafe((byte)value); - break; - case TypeCode.Int16: - writer.WriteValueSafe((short)value); - break; - case TypeCode.UInt16: - writer.WriteValueSafe((ushort)value); - break; - case TypeCode.Int32: - writer.WriteValueSafe((int)value); - break; - case TypeCode.UInt32: - writer.WriteValueSafe((uint)value); - break; - case TypeCode.Int64: - writer.WriteValueSafe((long)value); - break; - case TypeCode.UInt64: - writer.WriteValueSafe((ulong)value); - break; - } - return; - } - if (value is GameObject) - { - writer.WriteValueSafe((GameObject)value); - return; - } - if (value is NetworkObject) - { - writer.WriteValueSafe((NetworkObject)value); - return; - } - if (value is NetworkBehaviour) - { - writer.WriteValueSafe((NetworkBehaviour)value); - return; - } - if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - } - - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Get the required amount of space to write a GameObject - /// - /// - /// - public static int GetWriteSize(GameObject value) - { - return sizeof(ulong); - } - - /// - /// Get the required amount of space to write a GameObject - /// - /// - public static int GetGameObjectWriteSize() - { - return sizeof(ulong); - } - - /// - /// Write a GameObject - /// - /// The writer to use to write the value - /// The value to write - public static void WriteValue(this ref FastBufferWriter writer, in GameObject value) - { - value.TryGetComponent(out var networkObject); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); - } - - writer.WriteValue(networkObject.NetworkObjectId); - } - - /// - /// Write an array of GameObjects - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValue(this ref FastBufferWriter writer, GameObject[] value) - { - writer.WriteValue((int)value.Length); - foreach (var item in value) - { - writer.WriteValue(item); - } - } - - /// - /// Write a GameObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The writer to use to write the value - /// The value to write - public static void WriteValueSafe(this ref FastBufferWriter writer, in GameObject value) - { - value.TryGetComponent(out var networkObject); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); - } - - writer.WriteValueSafe(networkObject.NetworkObjectId); - } - - /// - /// Write an array of GameObjects - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject[] value) - { - writer.WriteValueSafe((int)value.Length); - foreach (var item in value) - { - writer.WriteValueSafe(item); - } - } - - /// - /// Get the required size to write a NetworkObject - /// - /// - /// - public static int GetWriteSize(NetworkObject value) - { - return sizeof(ulong); - } - - /// - /// Get the required size to write a NetworkObject - /// - /// - public static int GetNetworkObjectWriteSize() - { - return sizeof(ulong); - } - - - /// - /// Write a NetworkObject - /// - /// The writer to use to write the value - /// The value to write - public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject value) - { - if (!value.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); - } - - writer.WriteValue(value.NetworkObjectId); - } - - /// - /// Write an array of NetworkObjects - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValue(this ref FastBufferWriter writer, NetworkObject[] value) - { - writer.WriteValue((int)value.Length); - foreach (var item in value) - { - writer.WriteValue(item); - } - } - - /// - /// Write a NetworkObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The writer to use to write the value - /// The value to write - public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkObject value) - { - if (!value.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); - } - writer.WriteValueSafe(value.NetworkObjectId); - } - - /// - /// Write an array of NetworkObjects - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObject[] value) - { - writer.WriteValueSafe((int)value.Length); - foreach (var item in value) - { - writer.WriteValueSafe(item); - } - } - - /// - /// Get the required size to write a NetworkBehaviour - /// - /// - /// - public static int GetWriteSize(NetworkBehaviour value) - { - return sizeof(ulong) + sizeof(ushort); - } - - - /// - /// Get the required size to write a NetworkBehaviour - /// - /// - public static int GetNetworkBehaviourWriteSize() - { - return sizeof(ulong) + sizeof(ushort); - } - - - /// - /// Write a NetworkBehaviour - /// - /// The writer to use to write the value - /// The value to write - public static void WriteValue(this ref FastBufferWriter writer, in NetworkBehaviour value) - { - if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); - } - - writer.WriteValue(value.NetworkObjectId); - writer.WriteValue(value.NetworkBehaviourId); - } - - /// - /// Write an array of NetworkBehaviours - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour[] value) - { - writer.WriteValue((int)value.Length); - foreach (var item in value) - { - writer.WriteValue(item); - } - } - - /// - /// Write a NetworkBehaviour - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValueSafe(this ref FastBufferWriter writer, in NetworkBehaviour value) - { - if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); - } - - if (!writer.TryBeginWriteInternal(sizeof(ulong) + sizeof(ushort))) - { - throw new OverflowException("Writing past the end of the buffer"); - } - writer.WriteValue(value.NetworkObjectId); - writer.WriteValue(value.NetworkBehaviourId); - } - - /// - /// Write an array of NetworkBehaviours - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling TryBeginWrite. - /// - /// The writer to use to write the value - /// The value to write - /// - /// - public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehaviour[] value) - { - writer.WriteValueSafe((int)value.Length); - foreach (var item in value) - { - writer.WriteValueSafe(item); - } - } - - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index 079600300c..e94b6a4a12 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -1,6 +1,3 @@ -using System; -using UnityEngine; - namespace Unity.Netcode { public interface IBufferSerializerImplementation @@ -11,10 +8,6 @@ public interface IBufferSerializerImplementation ref FastBufferReader GetFastBufferReader(); ref FastBufferWriter GetFastBufferWriter(); - void SerializeValue(ref object value, Type type, bool isNullable = false); - void SerializeValue(ref GameObject value); - void SerializeValue(ref NetworkObject value); - void SerializeValue(ref NetworkBehaviour value); void SerializeValue(ref string s, bool oneByteChars = false); void SerializeValue(ref T[] array) where T : unmanaged; void SerializeValue(ref byte value); @@ -26,9 +19,6 @@ public interface IBufferSerializerImplementation void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new(); bool PreCheck(int amount); - void SerializeValuePreChecked(ref GameObject value); - void SerializeValuePreChecked(ref NetworkObject value); - void SerializeValuePreChecked(ref NetworkBehaviour value); void SerializeValuePreChecked(ref string s, bool oneByteChars = false); void SerializeValuePreChecked(ref T[] array) where T : unmanaged; void SerializeValuePreChecked(ref byte value); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs deleted file mode 100644 index fb861c91d7..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ /dev/null @@ -1,449 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Unity.Netcode -{ - /// - /// Registry for telling FastBufferWriter and FastBufferReader how to read types when passed to - /// WriteObject and ReadObject, as well as telling BytePacker and ByteUnpacker how to do it when passed to - /// WriteObjectPacked and ReadObjectPacked. - /// - /// These object-based serialization functions shouldn't be used if at all possible, but if they're required, - /// and you need to serialize a type that's not natively supported, you can register it with the dictionaries here: - /// - /// Serializers and Deserializers for FastBufferWriter and FasteBufferReader - /// SerializersPacked and DeserializersPacked for BytePacker and ByteUnpacker - /// - public static class SerializationTypeTable - { - public delegate void Serialize(ref FastBufferWriter writer, object value); - public delegate void Deserialize(ref FastBufferReader reader, out object value); - - public static Dictionary Serializers = new Dictionary - { - [typeof(byte)] = (ref FastBufferWriter writer, object value) => writer.WriteByteSafe((byte)value), - [typeof(sbyte)] = (ref FastBufferWriter writer, object value) => writer.WriteByteSafe((byte)(sbyte)value), - - [typeof(ushort)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ushort)value), - [typeof(short)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((short)value), - [typeof(uint)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((uint)value), - [typeof(int)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((int)value), - [typeof(ulong)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ulong)value), - [typeof(long)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((long)value), - - [typeof(float)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float)value), - [typeof(double)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double)value), - - [typeof(string)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((string)value), - - [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2)value), - [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3)value), - [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4)value), - [typeof(Color)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color)value), - [typeof(Color32)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color32)value), - [typeof(Ray)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray)value), - [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D)value), - [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion)value), - - [typeof(char)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char)value), - - [typeof(bool)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool)value), - - - [typeof(byte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((byte[])value), - [typeof(sbyte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((sbyte[])value), - - [typeof(ushort[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ushort[])value), - [typeof(short[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((short[])value), - [typeof(uint[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((uint[])value), - [typeof(int[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((int[])value), - [typeof(ulong[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ulong[])value), - [typeof(long[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((long[])value), - - [typeof(float[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float[])value), - [typeof(double[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double[])value), - - [typeof(Vector2[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2[])value), - [typeof(Vector3[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3[])value), - [typeof(Vector4[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4[])value), - [typeof(Color[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color[])value), - [typeof(Color32[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color32[])value), - [typeof(Ray[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray[])value), - [typeof(Ray2D[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D[])value), - [typeof(Quaternion[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion[])value), - - [typeof(char[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char[])value), - - [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), - }; - - public static Dictionary Deserializers = new Dictionary - { - [typeof(byte)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadByteSafe(out byte tmp); - value = tmp; - }, - [typeof(sbyte)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadByteSafe(out byte tmp); - value = (sbyte)tmp; - }, - - [typeof(ushort)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out ushort tmp); - value = tmp; - }, - [typeof(short)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out short tmp); - value = tmp; - }, - [typeof(uint)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out uint tmp); - value = tmp; - }, - [typeof(int)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out int tmp); - value = tmp; - }, - [typeof(ulong)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out ulong tmp); - value = tmp; - }, - [typeof(long)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out long tmp); - value = tmp; - }, - - [typeof(float)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out float tmp); - value = tmp; - }, - [typeof(double)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out double tmp); - value = tmp; - }, - - [typeof(string)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out string tmp); - value = tmp; - }, - - [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Vector2 tmp); - value = tmp; - }, - [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Vector3 tmp); - value = tmp; - }, - [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Vector4 tmp); - value = tmp; - }, - [typeof(Color)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Color tmp); - value = tmp; - }, - [typeof(Color32)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Color32 tmp); - value = tmp; - }, - [typeof(Ray)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Ray tmp); - value = tmp; - }, - [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Ray2D tmp); - value = tmp; - }, - [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Quaternion tmp); - value = tmp; - }, - - [typeof(char)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out char tmp); - value = tmp; - }, - - [typeof(bool)] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out bool tmp); - value = tmp; - }, - - - [typeof(byte[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out byte[] tmp); - value = tmp; - }, - [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out sbyte[] tmp); - value = tmp; - }, - - [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out ushort[] tmp); - value = tmp; - }, - [typeof(short[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out short[] tmp); - value = tmp; - }, - [typeof(uint[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out uint[] tmp); - value = tmp; - }, - [typeof(int[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out int[] tmp); - value = tmp; - }, - [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out ulong[] tmp); - value = tmp; - }, - [typeof(long[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out long[] tmp); - value = tmp; - }, - - [typeof(float[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out float[] tmp); - value = tmp; - }, - [typeof(double[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out double[] tmp); - value = tmp; - }, - - [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Vector2[] tmp); - value = tmp; - }, - [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Vector3[] tmp); - value = tmp; - }, - [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Vector4[] tmp); - value = tmp; - }, - [typeof(Color[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Color[] tmp); - value = tmp; - }, - [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Color32[] tmp); - value = tmp; - }, - [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Ray[] tmp); - value = tmp; - }, - [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Ray2D[] tmp); - value = tmp; - }, - [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out Quaternion[] tmp); - value = tmp; - }, - - [typeof(char[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out char[] tmp); - value = tmp; - }, - - [typeof(bool[])] = (ref FastBufferReader reader, out object value) => - { - reader.ReadValueSafe(out bool[] tmp); - value = tmp; - }, - }; - - public static Dictionary SerializersPacked = new Dictionary - { - [typeof(byte)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (byte)value), - [typeof(sbyte)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (byte)(sbyte)value), - - [typeof(ushort)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (ushort)value), - [typeof(short)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (short)value), - [typeof(uint)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (uint)value), - [typeof(int)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (int)value), - [typeof(ulong)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (ulong)value), - [typeof(long)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (long)value), - - [typeof(float)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (float)value), - [typeof(double)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (double)value), - - [typeof(string)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (string)value), - - [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector2)value), - [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector3)value), - [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector4)value), - [typeof(Color)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Color)value), - [typeof(Color32)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Color32)value), - [typeof(Ray)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray)value), - [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray2D)value), - [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Quaternion)value), - - [typeof(char)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (char)value), - - [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), - }; - - public static Dictionary DeserializersPacked = new Dictionary - { - [typeof(byte)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out byte tmp); - value = tmp; - }, - [typeof(sbyte)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out byte tmp); - value = (sbyte)tmp; - }, - - [typeof(ushort)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out ushort tmp); - value = tmp; - }, - [typeof(short)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out short tmp); - value = tmp; - }, - [typeof(uint)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out uint tmp); - value = tmp; - }, - [typeof(int)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out int tmp); - value = tmp; - }, - [typeof(ulong)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out ulong tmp); - value = tmp; - }, - [typeof(long)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out long tmp); - value = tmp; - }, - - [typeof(float)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out float tmp); - value = tmp; - }, - [typeof(double)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out double tmp); - value = tmp; - }, - - [typeof(string)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out string tmp); - value = tmp; - }, - - [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Vector2 tmp); - value = tmp; - }, - [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Vector3 tmp); - value = tmp; - }, - [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Vector4 tmp); - value = tmp; - }, - [typeof(Color)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Color tmp); - value = tmp; - }, - [typeof(Color32)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Color32 tmp); - value = tmp; - }, - [typeof(Ray)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Ray tmp); - value = tmp; - }, - [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Ray2D tmp); - value = tmp; - }, - [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out Quaternion tmp); - value = tmp; - }, - - [typeof(char)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out char tmp); - value = tmp; - }, - - [typeof(bool)] = (ref FastBufferReader reader, out object value) => - { - ByteUnpacker.ReadValuePacked(ref reader, out bool tmp); - value = tmp; - }, - }; - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta deleted file mode 100644 index 58c25d1646..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 49ee6a0e2ea6e9441a74b173c31cf389 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 18dbdbe8a8..1cbbd686b5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Unity.Netcode.Messages; using UnityEngine; namespace Unity.Netcode diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs deleted file mode 100644 index 04cac54419..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Netcode -{ - /// - /// For each usage of FixedUnmanagedArray, a storage class needs to be created for it. - /// Rather than providing a huge list of predefined storage classes, each use should just - /// create their own at the correct size (in bytes). The size should be an even multiple - /// of the value size being stored. - /// - /// Example: - /// - /// [StructLayout(LayoutKind.Explicit, Size = 256 * sizeof(int))] - /// struct FixedStorageInt256 : IFixedArrayStorage - /// { - /// } - /// - public interface IFixedArrayStorage - { - - } - - public struct FixedUnmanagedArray : IReadOnlyList - where TPropertyType : unmanaged - where TStorageType : unmanaged, IFixedArrayStorage - { - private int m_Length; - private TStorageType m_Data; - - public int Count - { - get { return m_Length; } - set { m_Length = value; } - } - - public unsafe int Capacity => sizeof(TStorageType) / sizeof(TPropertyType); - - public bool IsReadOnly => false; - - public unsafe TPropertyType[] ToArray() - { - var ret = new TPropertyType[Count]; - fixed (TPropertyType* b = ret) - { - fixed (TStorageType* ptr = &m_Data) - { - UnsafeUtility.MemCpy(b, ptr, Count * sizeof(TPropertyType)); - } - } - return ret; - } - - public unsafe FixedUnmanagedArray(TPropertyType* seedData, int size) - { - if (size > sizeof(TStorageType)) - { - throw new OverflowException("Seed data was larger than provided storage class."); - } - - m_Data = new TStorageType(); - fixed (TStorageType* ptr = &m_Data) - { - UnsafeUtility.MemCpy(ptr, seedData, size); - } - - m_Length = size; - } - - public unsafe FixedUnmanagedArray(TPropertyType[] seedData) - { - if (seedData.Length > sizeof(TStorageType)) - { - throw new OverflowException("Seed data was larger than provided storage class."); - } - m_Data = new TStorageType(); - fixed (TStorageType* ptr = &m_Data) - fixed (TPropertyType* seedPtr = seedData) - { - UnsafeUtility.MemCpy(ptr, seedPtr, seedData.Length); - } - - m_Length = seedData.Length; - } - - public unsafe FixedUnmanagedArray(TPropertyType[] seedData, int size) - { - if (size > sizeof(TStorageType)) - { - throw new OverflowException("Seed data was larger than provided storage class."); - } - - if (size > seedData.Length) - { - throw new ArgumentException("Size cannot be greater than seed data's length."); - } - - m_Data = new TStorageType(); - fixed (TStorageType* ptr = &m_Data) - fixed (TPropertyType* seedPtr = seedData) - { - UnsafeUtility.MemCpy(ptr, seedPtr, size); - } - - m_Length = size; - } - - public unsafe TPropertyType* GetArrayPtr() - { - fixed (TStorageType* ptr = &m_Data) - { - return (TPropertyType*)ptr; - } - } - - public unsafe TPropertyType this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - fixed (TStorageType* ptr = &m_Data) - { - var reinterpretPtr = (TPropertyType*)ptr; - return reinterpretPtr[index]; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - fixed (TStorageType* ptr = &m_Data) - { - var reinterpretPtr = (TPropertyType*)ptr; - reinterpretPtr[index] = value; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ref TPropertyType GetValueRef(int index) - { - fixed (TStorageType* ptr = &m_Data) - { - var reinterpretPtr = (TPropertyType*)ptr; - return ref reinterpretPtr[index]; - } - } - - public void Add(TPropertyType value) - { - if (m_Length == Capacity) - { - throw new OverflowException("The FixedUnmanagedArray is full."); - } - - this[m_Length++] = value; - } - - public TPropertyType Pop() - { - return this[--m_Length]; - } - - public void Clear() - { - m_Length = 0; - } - - public IEnumerator GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta deleted file mode 100644 index 5f3b5fd9b4..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/FixedUnmanagedArray.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7bde9c91128507449a535c7df4a029c8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index 5a627b28f9..2243caf39c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -2,7 +2,7 @@ namespace Unity.Netcode { - public struct Ref where T : unmanaged + internal struct Ref where T : unmanaged { private unsafe T* m_Value; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs index 9794ca415e..ba95422cb4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs @@ -9,7 +9,7 @@ namespace Unity.Netcode.EditorTests { public class MessageReceivingTests { - [Bind(typeof(MessageReceivingTests))] + [IgnoreMessageIfSystemOwnerIsNotOfType(typeof(MessageReceivingTests))] private struct TestMessage : INetworkMessage { public int A; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs index ccc950d186..3a5aa556f2 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -15,7 +15,7 @@ private class MessagingSystemOwnerTwo } - [Bind(typeof(MessagingSystemOwnerOne))] + [IgnoreMessageIfSystemOwnerIsNotOfType(typeof(MessagingSystemOwnerOne))] private struct TestMessageOne : INetworkMessage { public int A; @@ -33,7 +33,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) } } - [Bind(typeof(MessagingSystemOwnerOne))] + [IgnoreMessageIfSystemOwnerIsNotOfType(typeof(MessagingSystemOwnerOne))] private struct TestMessageTwo : INetworkMessage { public int A; @@ -51,7 +51,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) } } - [Bind(typeof(MessagingSystemOwnerTwo))] + [IgnoreMessageIfSystemOwnerIsNotOfType(typeof(MessagingSystemOwnerTwo))] private struct TestMessageThree : INetworkMessage { public int A; @@ -69,7 +69,7 @@ public static void Receive(ref FastBufferReader reader, NetworkContext context) } } - [Bind(null)] + [IgnoreMessageIfSystemOwnerIsNotOfType(null)] private struct TestMessageFour : INetworkMessage { public int A; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs index 419b2ba6b2..d7811d19bb 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs @@ -8,7 +8,7 @@ namespace Unity.Netcode.EditorTests { public class MessageSendingTests { - [Bind(typeof(MessageSendingTests))] + [IgnoreMessageIfSystemOwnerIsNotOfType(typeof(MessageSendingTests))] private struct TestMessage : INetworkMessage { public int A; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index fa067d73a6..e08d038833 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,6 +1,5 @@ using System; using NUnit.Framework; -using Unity.Netcode.EditorTests; using UnityEngine; using Random = System.Random; @@ -77,8 +76,7 @@ protected struct TestStruct public enum WriteType { WriteDirect, - WriteSafe, - WriteAsObject + WriteSafe } #endregion @@ -87,14 +85,10 @@ public enum WriteType protected abstract void RunTypeTestSafe(T valueToTest) where T : unmanaged; - protected abstract void RunObjectTypeTest(T valueToTest) where T : unmanaged; - protected abstract void RunTypeArrayTest(T[] valueToTest) where T : unmanaged; protected abstract void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged; - protected abstract void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged; - #region Helpers protected TestStruct GetTestStruct() { @@ -118,37 +112,6 @@ protected TestStruct GetTestStruct() return testStruct; } - protected delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, - NetworkObject networkObject); - protected void RunGameObjectTest(GameObjectTestDelegate testCode) - { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkManager.SetSingleton(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartHost(); - - try - { - testCode(obj, networkBehaviour, networkObject); - } - finally - { - UnityEngine.Object.DestroyImmediate(obj); - networkManager.Shutdown(); - } - } #endregion public void BaseTypeTest(Type testType, WriteType writeType) @@ -165,9 +128,6 @@ void RunTypeTestLocal(T val, WriteType wt) where T : unmanaged case WriteType.WriteSafe: RunTypeTestSafe(val); break; - default: - RunObjectTypeTest(val); - break; } } @@ -290,24 +250,7 @@ void RunTypeTestLocal(T val, WriteType wt) where T : unmanaged } else if (testType == typeof(TestStruct)) { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => - { - reader.ReadValueSafe(out TestStruct value); - obj = value; - }; - try - { - RunTypeTestLocal(GetTestStruct(), writeType); - } - finally - { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); - SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); - } + RunTypeTestLocal(GetTestStruct(), writeType); } else { @@ -328,9 +271,6 @@ void RunTypeTestLocal(T[] val, WriteType wt) where T : unmanaged case WriteType.WriteSafe: RunTypeArrayTestSafe(val); break; - default: - RunObjectTypeArrayTest(val); - break; } } @@ -632,28 +572,11 @@ void RunTypeTestLocal(T[] val, WriteType wt) where T : unmanaged } else if (testType == typeof(TestStruct)) { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => - { - reader.ReadValueSafe(out TestStruct value); - obj = value; - }; - try - { - RunTypeTestLocal(new[] { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }, writeType); - } - finally - { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); - SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); - } + RunTypeTestLocal(new[] { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }, writeType); } else { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 1847dd0954..9151a67aba 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -76,36 +76,6 @@ public unsafe void TestGetUnderlyingStructs() } } - // Not reimplementing the entire suite of all value tests for BufferSerializer since they're already tested - // for the underlying structures. These are just basic tests to make sure the correct underlying functions - // are being called. - [Test] - public void TestSerializingObjects() - { - var random = new Random(); - int value = random.Next(); - object asObj = value; - - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - serializer.SerializeValue(ref asObj, typeof(int)); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - object readValue = 0; - deserializer.SerializeValue(ref readValue, typeof(int)); - - Assert.AreEqual(value, readValue); - } - } - } - [Test] public void TestSerializingValues() { @@ -418,227 +388,5 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) networkManager.Shutdown(); } } - - [Test] - public void TestSerializingGameObjects() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - serializer.SerializeValue(ref obj); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - GameObject readValue = null; - deserializer.SerializeValue(ref readValue); - - Assert.AreEqual(obj, readValue); - } - } - } - ); - } - - [Test] - public void TestSerializingNetworkObjects() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - serializer.SerializeValue(ref networkObject); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - NetworkObject readValue = null; - deserializer.SerializeValue(ref readValue); - - Assert.AreEqual(networkObject, readValue); - } - } - } - ); - } - - [Test] - public void TestSerializingNetworkBehaviours() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - serializer.SerializeValue(ref networkBehaviour); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - NetworkBehaviour readValue = null; - deserializer.SerializeValue(ref readValue); - - Assert.AreEqual(networkBehaviour, readValue); - } - } - } - ); - } - - [Test] - public void TestSerializingGameObjectsPreChecked() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - try - { - serializer.SerializeValuePreChecked(ref obj); - } - catch (OverflowException e) - { - // Pass - } - - Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(obj))); - serializer.SerializeValuePreChecked(ref obj); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - GameObject readValue = null; - try - { - deserializer.SerializeValuePreChecked(ref readValue); - } - catch (OverflowException e) - { - // Pass - } - - Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); - deserializer.SerializeValuePreChecked(ref readValue); - - Assert.AreEqual(obj, readValue); - } - } - } - ); - } - - [Test] - public void TestSerializingNetworkObjectsPreChecked() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - try - { - serializer.SerializeValuePreChecked(ref networkObject); - } - catch (OverflowException e) - { - // Pass - } - - Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(networkObject))); - serializer.SerializeValuePreChecked(ref networkObject); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - NetworkObject readValue = null; - try - { - deserializer.SerializeValuePreChecked(ref readValue); - } - catch (OverflowException e) - { - // Pass - } - - Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); - deserializer.SerializeValuePreChecked(ref readValue); - - Assert.AreEqual(networkObject, readValue); - } - } - } - ); - } - - [Test] - public void TestSerializingNetworkBehavioursPreChecked() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(100, Allocator.Temp); - using (writer) - { - var serializer = - new BufferSerializer(new BufferSerializerWriter(ref writer)); - try - { - serializer.SerializeValuePreChecked(ref networkBehaviour); - } - catch (OverflowException e) - { - // Pass - } - - Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); - serializer.SerializeValuePreChecked(ref networkBehaviour); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - var deserializer = - new BufferSerializer(new BufferSerializerReader(ref reader)); - NetworkBehaviour readValue = null; - try - { - deserializer.SerializeValuePreChecked(ref readValue); - } - catch (OverflowException e) - { - // Pass - } - - Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); - deserializer.SerializeValuePreChecked(ref readValue); - - Assert.AreEqual(networkBehaviour, readValue); - } - } - } - ); - } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 1c2ff159e9..36111e116b 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -229,24 +229,6 @@ private unsafe void RunTypeTest(T value) where T : unmanaged } } - private unsafe void RunObjectTypeTest(T value) where T : unmanaged - { - var writer = new FastBufferWriter(sizeof(T) * 2, Allocator.Temp); - using (writer) - { - BytePacker.WriteObjectPacked(ref writer, value); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); - Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, (T)outVal); - } - } - } - - [Test] public void TestPacking64BitsUnsigned() @@ -851,10 +833,6 @@ public void TestPackingBasicTypes( { RunTypeTest(b); } - else - { - RunObjectTypeTest(b); - } } else if (testType == typeof(sbyte)) { @@ -863,10 +841,6 @@ public void TestPackingBasicTypes( { RunTypeTest(sb); } - else - { - RunObjectTypeTest(sb); - } } else if (testType == typeof(short)) { @@ -875,10 +849,6 @@ public void TestPackingBasicTypes( { RunTypeTest(s); } - else - { - RunObjectTypeTest(s); - } } else if (testType == typeof(ushort)) { @@ -887,10 +857,6 @@ public void TestPackingBasicTypes( { RunTypeTest(us); } - else - { - RunObjectTypeTest(us); - } } else if (testType == typeof(int)) { @@ -899,10 +865,6 @@ public void TestPackingBasicTypes( { RunTypeTest(i); } - else - { - RunObjectTypeTest(i); - } } else if (testType == typeof(uint)) { @@ -911,10 +873,6 @@ public void TestPackingBasicTypes( { RunTypeTest(ui); } - else - { - RunObjectTypeTest(ui); - } } else if (testType == typeof(long)) { @@ -923,10 +881,6 @@ public void TestPackingBasicTypes( { RunTypeTest(l); } - else - { - RunObjectTypeTest(l); - } } else if (testType == typeof(ulong)) { @@ -935,10 +889,6 @@ public void TestPackingBasicTypes( { RunTypeTest(ul); } - else - { - RunObjectTypeTest(ul); - } } else if (testType == typeof(bool)) { @@ -946,10 +896,6 @@ public void TestPackingBasicTypes( { RunTypeTest(true); } - else - { - RunObjectTypeTest(true); - } } else if (testType == typeof(char)) { @@ -958,20 +904,12 @@ public void TestPackingBasicTypes( { RunTypeTest(c); } - else - { - RunObjectTypeTest(c); - } c = '\u263a'; if (writeType == WriteType.WriteDirect) { RunTypeTest(c); } - else - { - RunObjectTypeTest(c); - } } else if (testType == typeof(float)) { @@ -980,10 +918,6 @@ public void TestPackingBasicTypes( { RunTypeTest(f); } - else - { - RunObjectTypeTest(f); - } } else if (testType == typeof(double)) { @@ -992,10 +926,6 @@ public void TestPackingBasicTypes( { RunTypeTest(d); } - else - { - RunObjectTypeTest(d); - } } else if (testType == typeof(ByteEnum)) { @@ -1004,10 +934,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(SByteEnum)) { @@ -1016,10 +942,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(ShortEnum)) { @@ -1028,10 +950,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(UShortEnum)) { @@ -1040,10 +958,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(IntEnum)) { @@ -1052,10 +966,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(UIntEnum)) { @@ -1064,10 +974,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(LongEnum)) { @@ -1076,10 +982,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(ULongEnum)) { @@ -1088,10 +990,6 @@ public void TestPackingBasicTypes( { RunTypeTest(e); } - else - { - RunObjectTypeTest(e); - } } else if (testType == typeof(Vector2)) { @@ -1100,10 +998,6 @@ public void TestPackingBasicTypes( { RunTypeTest(v); } - else - { - RunObjectTypeTest(v); - } } else if (testType == typeof(Vector3)) { @@ -1112,10 +1006,6 @@ public void TestPackingBasicTypes( { RunTypeTest(v); } - else - { - RunObjectTypeTest(v); - } } else if (testType == typeof(Vector4)) { @@ -1124,10 +1014,6 @@ public void TestPackingBasicTypes( { RunTypeTest(v); } - else - { - RunObjectTypeTest(v); - } } else if (testType == typeof(Quaternion)) { @@ -1136,10 +1022,6 @@ public void TestPackingBasicTypes( { RunTypeTest(v); } - else - { - RunObjectTypeTest(v); - } } else if (testType == typeof(Color)) { @@ -1148,10 +1030,6 @@ public void TestPackingBasicTypes( { RunTypeTest(v); } - else - { - RunObjectTypeTest(v); - } } else if (testType == typeof(Color32)) { @@ -1160,10 +1038,6 @@ public void TestPackingBasicTypes( { RunTypeTest(v); } - else - { - RunObjectTypeTest(v); - } } else if (testType == typeof(Ray)) { @@ -1192,26 +1066,6 @@ public void TestPackingBasicTypes( } } } - else - { - unsafe - { - var writer = new FastBufferWriter(sizeof(Ray) * 2, Allocator.Temp); - using (writer) - { - BytePacker.WriteObjectPacked(ref writer, v); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); - Assert.AreEqual(v.origin, ((Ray)outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); - Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); - } - } - } - } } else if (testType == typeof(Ray2D)) { @@ -1239,25 +1093,6 @@ public void TestPackingBasicTypes( } } } - else - { - unsafe - { - var writer = new FastBufferWriter(sizeof(Ray2D) * 2, Allocator.Temp); - using (writer) - { - BytePacker.WriteObjectPacked(ref writer, v); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); - Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); - } - } - } - } } else { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 0f3e36e2e8..56bcb0c82b 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -102,28 +102,6 @@ protected override unsafe void RunTypeTestSafe(T valueToTest) } } - protected override unsafe void RunObjectTypeTest(T valueToTest) - { - var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - - using (writer) - { - Assert.AreEqual(sizeof(T), writeSize); - - var failMessage = $"RunObjectTypeTest failed with type {typeof(T)} and value {valueToTest}"; - writer.WriteObject(valueToTest); - - var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - - using (reader) - { - reader.ReadObject(out object result, typeof(T)); - Assert.AreEqual(valueToTest, result); - } - } - } - private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T : unmanaged { Assert.AreEqual(value.Length, compareValue.Length); @@ -186,31 +164,6 @@ protected override unsafe void RunTypeArrayTestSafe(T[] valueToTest) } } - protected override unsafe void RunObjectTypeArrayTest(T[] valueToTest) - { - var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - // Extra byte for WriteObject adding isNull flag - var writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); - using (writer) - { - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); - - writer.WriteObject(valueToTest); - - WriteCheckBytes(ref writer, writeSize + 1); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadObject(out object result, typeof(T[])); - VerifyArrayEquality(valueToTest, (T[])result, 0); - - VerifyCheckBytes(ref reader, writeSize + 1); - } - } - } #endregion #region Tests @@ -242,7 +195,6 @@ public void GivenFastBufferWriterContainingValue_WhenReadingArrayOfUnmanagedElem [TestCase(false, WriteType.WriteDirect)] [TestCase(false, WriteType.WriteSafe)] - [TestCase(false, WriteType.WriteAsObject)] [TestCase(true, WriteType.WriteDirect)] [TestCase(true, WriteType.WriteSafe)] public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesWhatWasWritten(bool oneByteChars, WriteType writeType) @@ -263,10 +215,6 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW case WriteType.WriteSafe: writer.WriteValueSafe(valueToTest, oneByteChars); break; - case WriteType.WriteAsObject: - writer.WriteObject(valueToTest); - serializedValueSize += 1; - break; } WriteCheckBytes(ref writer, serializedValueSize); @@ -286,10 +234,6 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW case WriteType.WriteSafe: reader.ReadValueSafe(out result, oneByteChars); break; - case WriteType.WriteAsObject: - reader.ReadObject(out object resultObj, typeof(string), oneByteChars); - result = (string)resultObj; - break; } Assert.AreEqual(valueToTest, result); @@ -869,147 +813,6 @@ public void WhenReadingAfterSeeking_TheNewReadComesFromTheCorrectPosition() } } - [Test] - public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObject_TheSameObjectIsRetrieved([Values] WriteType writeType) - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject) + 1, Allocator.Temp); - using (writer) - { - switch (writeType) - { - case WriteType.WriteDirect: - Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); - writer.WriteValue(networkObject); - break; - case WriteType.WriteSafe: - writer.WriteValueSafe(networkObject); - break; - case WriteType.WriteAsObject: - writer.WriteObject(networkObject); - break; - } - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetNetworkObjectWriteSize())); - NetworkObject result = null; - switch (writeType) - { - case WriteType.WriteDirect: - Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(networkObject))); - reader.ReadValue(out result); - break; - case WriteType.WriteSafe: - reader.ReadValueSafe(out result); - break; - case WriteType.WriteAsObject: - reader.ReadObject(out object resultObj, typeof(NetworkObject)); - result = (NetworkObject)resultObj; - break; - } - Assert.AreSame(result, networkObject); - } - } - }); - } - - [Test] - public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_TheSameObjectIsRetrieved([Values] WriteType writeType) - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj) + 1, Allocator.Temp); - using (writer) - { - switch (writeType) - { - case WriteType.WriteDirect: - Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); - writer.WriteValue(obj); - break; - case WriteType.WriteSafe: - writer.WriteValueSafe(obj); - break; - case WriteType.WriteAsObject: - writer.WriteObject(obj); - break; - } - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetGameObjectWriteSize())); - GameObject result = null; - switch (writeType) - { - case WriteType.WriteDirect: - Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(obj))); - reader.ReadValue(out result); - break; - case WriteType.WriteSafe: - reader.ReadValueSafe(out result); - break; - case WriteType.WriteAsObject: - reader.ReadObject(out object resultObj, typeof(GameObject)); - result = (GameObject)resultObj; - break; - } - Assert.AreSame(result, obj); - } - } - }); - } - - [Test] - public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkBehaviour_TheSameObjectIsRetrieved([Values] WriteType writeType) - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); - using (writer) - { - switch (writeType) - { - case WriteType.WriteDirect: - Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); - writer.WriteValue(networkBehaviour); - break; - case WriteType.WriteSafe: - writer.WriteValueSafe(networkBehaviour); - break; - case WriteType.WriteAsObject: - writer.WriteObject(networkBehaviour); - break; - } - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetNetworkBehaviourWriteSize())); - NetworkBehaviour result = null; - switch (writeType) - { - case WriteType.WriteDirect: - Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); - reader.ReadValue(out result); - break; - case WriteType.WriteSafe: - reader.ReadValueSafe(out result); - break; - case WriteType.WriteAsObject: - reader.ReadObject(out object resultObj, typeof(NetworkBehaviour)); - result = (NetworkBehaviour)resultObj; - break; - } - Assert.AreSame(result, networkBehaviour); - } - } - }); - } - [Test] public void WhenCallingTryBeginReadInternal_AllowedReadPositionDoesNotMoveBackward() { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index f1d3897737..f1f49668f3 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -104,22 +104,6 @@ protected override unsafe void RunTypeTestSafe(T valueToTest) } } - protected override unsafe void RunObjectTypeTest(T valueToTest) - { - var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - - using (writer) - { - Assert.AreEqual(sizeof(T), writeSize); - - var failMessage = $"RunObjectTypeTest failed with type {typeof(T)} and value {valueToTest}"; - writer.WriteObject(valueToTest); - - CommonChecks(ref writer, valueToTest, writeSize, failMessage); - } - } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged { int* sizeValue = (int*)(unsafePtr + offset); @@ -177,29 +161,6 @@ protected override unsafe void RunTypeArrayTestSafe(T[] valueToTest) VerifyCheckBytes(underlyingArray, writeSize); } } - - protected override unsafe void RunObjectTypeArrayTest(T[] valueToTest) - { - var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - // Extra byte for WriteObject adding isNull flag - var writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); - using (writer) - { - - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); - - writer.WriteObject(valueToTest); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); - - WriteCheckBytes(ref writer, writeSize + sizeof(byte)); - - VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), sizeof(byte)); - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, writeSize + sizeof(byte)); - } - } #endregion @@ -232,7 +193,6 @@ public void WhenWritingArrayOfUnmanagedElementType_ArrayIsWrittenCorrectly( [TestCase(false, WriteType.WriteDirect)] [TestCase(false, WriteType.WriteSafe)] - [TestCase(false, WriteType.WriteAsObject)] [TestCase(true, WriteType.WriteDirect)] [TestCase(true, WriteType.WriteSafe)] public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, WriteType writeType) @@ -254,11 +214,6 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, case WriteType.WriteSafe: writer.WriteValueSafe(valueToTest, oneByteChars); break; - case WriteType.WriteAsObject: - writer.WriteObject(valueToTest); - // account for isNull byte - offset = sizeof(byte); - break; } @@ -1101,104 +1056,6 @@ public void WhenTryingToWritePastMaxCapacity_GrowthDoesNotOccurAndTryBeginWriteR } } - [Test] - public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values] WriteType writeType) - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); - - var offset = 0; - switch (writeType) - { - case WriteType.WriteDirect: - writer.WriteValue(networkBehaviour); - break; - case WriteType.WriteSafe: - writer.WriteValueSafe(networkBehaviour); - break; - case WriteType.WriteAsObject: - writer.WriteObject(networkBehaviour); - // account for isNull byte - offset = 1; - break; - } - - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + offset, writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong) + offset, sizeof(ushort)); - } - }); - } - - [Test] - public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType writeType) - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject) + 1, Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); - - var offset = 0; - switch (writeType) - { - case WriteType.WriteDirect: - writer.WriteValue(networkObject); - break; - case WriteType.WriteSafe: - writer.WriteValueSafe(networkObject); - break; - case WriteType.WriteAsObject: - writer.WriteObject(networkObject); - // account for isNull byte - offset = 1; - break; - } - - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject) + offset, writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); - } - }); - } - - [Test] - public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType writeType) - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj) + 1, Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); - - var offset = 0; - switch (writeType) - { - case WriteType.WriteDirect: - writer.WriteValue(obj); - break; - case WriteType.WriteSafe: - writer.WriteValueSafe(obj); - break; - case WriteType.WriteAsObject: - writer.WriteObject(obj); - // account for isNull byte - offset = 1; - break; - } - - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj) + offset, writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); - } - }); - } - [Test] public void WhenCallingTryBeginWriteInternal_AllowedWritePositionDoesNotMoveBackward() { diff --git a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs index f3811856da..e5389ce4f1 100644 --- a/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs +++ b/testproject/Assets/Tests/Runtime/RpcTestsAutomated.cs @@ -5,7 +5,6 @@ using UnityEngine; using UnityEngine.TestTools; using TestProject.ManualTests; -using Unity.Collections; using Unity.Netcode.RuntimeTests; using Unity.Netcode; using Debug = UnityEngine.Debug; @@ -22,7 +21,6 @@ public class RpcTestsAutomated : BaseMultiInstanceTest [UnitySetUp] public override IEnumerator Setup() { - NativeLeakDetection.Mode = NativeLeakDetectionMode.EnabledWithStackTrace; yield break; } From 1aaeb80b68e0d459cb18d7038f005ffb8d73278c Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 16:07:06 -0500 Subject: [PATCH 47/58] Fixed snapshot stuff and also an outdated comment. --- .../Runtime/Core/SnapshotSystem.cs | 14 ++-- .../Messaging/Messages/ServerLogMessage.cs | 2 +- .../Messaging/Messages/SnapshotDataMessage.cs | 68 ++++++++++++++----- 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index 740f285115..9d2b1ce815 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -811,8 +811,8 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) clientData.NextDespawnIndex = 0; } - message.Spawns = new NativeArray(m_Snapshot.NumSpawns, Allocator.TempJob); - message.Despawns = new NativeArray(m_Snapshot.NumDespawns, Allocator.TempJob); + message.Spawns = new NativeList(m_Snapshot.NumSpawns, Allocator.TempJob); + message.Despawns = new NativeList(m_Snapshot.NumDespawns, Allocator.TempJob); var spawnUsage = 0; for (var j = 0; j < m_Snapshot.NumSpawns && !overSize; j++) @@ -831,7 +831,7 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) break; } var sentSpawn = m_Snapshot.GetSpawnData(clientData, in m_Snapshot.Spawns[index], out var spawn); - message.Spawns[j] = spawn; + message.Spawns.Add(spawn); m_Snapshot.Spawns[index].TimesWritten++; clientData.SentSpawns.Add(sentSpawn); @@ -864,7 +864,7 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) break; } var sentDespawn = m_Snapshot.GetDespawnData(clientData, in m_Snapshot.Despawns[index], out var despawn); - message.Despawns[j] = despawn; + message.Despawns.Add(despawn); m_Snapshot.Despawns[index].TimesWritten++; clientData.SentSpawns.Add(sentDespawn); despawnWritten++; @@ -879,12 +879,12 @@ private void WriteSpawns(ref SnapshotDataMessage message, ulong clientId) /// The message to write the index to private void WriteIndex(ref SnapshotDataMessage message) { - message.Entries = new NativeArray(m_Snapshot.LastEntry, Allocator.TempJob); + message.Entries = new NativeList(m_Snapshot.LastEntry, Allocator.TempJob); for (var i = 0; i < m_Snapshot.LastEntry; i++) { var entryMeta = m_Snapshot.Entries[i]; var entry = entryMeta.Key; - message.Entries[i] = new SnapshotDataMessage.EntryData + message.Entries.Add(new SnapshotDataMessage.EntryData { NetworkObjectId = entry.NetworkObjectId, BehaviourIndex = entry.BehaviourIndex, @@ -892,7 +892,7 @@ private void WriteIndex(ref SnapshotDataMessage message) TickWritten = entry.TickWritten, Position = entryMeta.Position, Length = entryMeta.Length - }; + }); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs index 3dddc93d98..22119f447c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ServerLogMessage.cs @@ -3,7 +3,7 @@ namespace Unity.Netcode internal struct ServerLogMessage : INetworkMessage { public NetworkLog.LogType LogType; - // It'd be lovely to be able to replace this with FixedUnmanagedArray... + // It'd be lovely to be able to replace this with FixedString or NativeArray... // But it's not really practical. On the sending side, the user is likely to want // to work with strings and would need to convert, and on the receiving side, // we'd have to convert it to a string to be able to pass it to the log system. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs index d1d84d49e4..3ece750371 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SnapshotDataMessage.cs @@ -1,3 +1,4 @@ +using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; @@ -32,7 +33,7 @@ public struct EntryData public ushort Length; } - public NativeArray Entries; + public NativeList Entries; public struct SpawnData { @@ -50,7 +51,7 @@ public struct SpawnData public int TickWritten; } - public NativeArray Spawns; + public NativeList Spawns; public struct DespawnData { @@ -58,10 +59,28 @@ public struct DespawnData public int TickWritten; } - public NativeArray Despawns; + public NativeList Despawns; public unsafe void Serialize(ref FastBufferWriter writer) { + if (!writer.TryBeginWrite( + FastBufferWriter.GetWriteSize(CurrentTick) + + FastBufferWriter.GetWriteSize(Sequence) + + FastBufferWriter.GetWriteSize(Range) + Range + + FastBufferWriter.GetWriteSize(Ack) + + FastBufferWriter.GetWriteSize() + + Entries.Length * sizeof(EntryData) + + FastBufferWriter.GetWriteSize() + + Spawns.Length * sizeof(SpawnData) + + FastBufferWriter.GetWriteSize() + + Despawns.Length * sizeof(DespawnData) + )) + { + Entries.Dispose(); + Spawns.Dispose(); + Despawns.Dispose(); + throw new OverflowException($"Not enough space to serialize {nameof(SnapshotDataMessage)}"); + } writer.WriteValue(CurrentTick); writer.WriteValue(Sequence); @@ -77,31 +96,46 @@ public unsafe void Serialize(ref FastBufferWriter writer) writer.WriteValue((ushort)Despawns.Length); writer.WriteBytes((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData)); + + Entries.Dispose(); + Spawns.Dispose(); + Despawns.Dispose(); } public static unsafe void Receive(ref FastBufferReader reader, NetworkContext context) { var networkManager = (NetworkManager)context.SystemOwner; var message = new SnapshotDataMessage(); + if (!reader.TryBeginRead( + FastBufferWriter.GetWriteSize(message.CurrentTick) + + FastBufferWriter.GetWriteSize(message.Sequence) + + FastBufferWriter.GetWriteSize(message.Range) + )) + { + throw new OverflowException($"Not enough space to deserialize {nameof(SnapshotDataMessage)}"); + } reader.ReadValue(out message.CurrentTick); reader.ReadValue(out message.Sequence); reader.ReadValue(out message.Range); message.ReceiveMainBuffer = new NativeArray(message.Range, Allocator.Temp); - reader.ReadBytes((byte*)message.ReceiveMainBuffer.GetUnsafePtr(), message.Range); - reader.ReadValue(out message.Ack); - - reader.ReadValue(out ushort length); - message.Entries = new NativeArray(length, Allocator.Temp); - reader.ReadBytes((byte*)message.Entries.GetUnsafePtr(), message.Entries.Length * sizeof(EntryData)); - - reader.ReadValue(out length); - message.Spawns = new NativeArray(length, Allocator.Temp); - reader.ReadBytes((byte*)message.Spawns.GetUnsafePtr(), message.Spawns.Length * sizeof(SpawnData)); - - reader.ReadValue(out length); - message.Despawns = new NativeArray(length, Allocator.Temp); - reader.ReadBytes((byte*)message.Despawns.GetUnsafePtr(), message.Despawns.Length * sizeof(DespawnData)); + reader.ReadBytesSafe((byte*)message.ReceiveMainBuffer.GetUnsafePtr(), message.Range); + reader.ReadValueSafe(out message.Ack); + + reader.ReadValueSafe(out ushort length); + message.Entries = new NativeList(length, Allocator.Temp); + message.Entries.Length = length; + reader.ReadBytesSafe((byte*)message.Entries.GetUnsafePtr(), message.Entries.Length * sizeof(EntryData)); + + reader.ReadValueSafe(out length); + message.Spawns = new NativeList(length, Allocator.Temp); + message.Spawns.Length = length; + reader.ReadBytesSafe((byte*)message.Spawns.GetUnsafePtr(), message.Spawns.Length * sizeof(SpawnData)); + + reader.ReadValueSafe(out length); + message.Despawns = new NativeList(length, Allocator.Temp); + message.Despawns.Length = length; + reader.ReadBytesSafe((byte*)message.Despawns.GetUnsafePtr(), message.Despawns.Length * sizeof(DespawnData)); using (message.ReceiveMainBuffer) using (message.Entries) From a5f54d7449a37d720742232c7fa1b8c665d1c362 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 16:13:02 -0500 Subject: [PATCH 48/58] More feedback. --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 4 ++-- .../Editor/CodeGen/RuntimeAccessModifiersILPP.cs | 6 +++--- .../Runtime/Core/NetworkBehaviour.cs | 7 ++++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 98f9a8ee0e..ea5b4c819a 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -141,8 +141,8 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table); private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage); - private const string k_NetworkBehaviour_SendServerRpc = nameof(NetworkBehaviour.SendServerRpc); - private const string k_NetworkBehaviour_SendClientRpc = nameof(NetworkBehaviour.SendClientRpc); + private const string k_NetworkBehaviour_SendServerRpc = nameof(NetworkBehaviour.__sendServerRpc); + private const string k_NetworkBehaviour_SendClientRpc = nameof(NetworkBehaviour.__sendClientRpc); private const string k_NetworkBehaviour_NetworkManager = nameof(NetworkBehaviour.NetworkManager); private const string k_NetworkBehaviour_OwnerClientId = nameof(NetworkBehaviour.OwnerClientId); diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index ba6d70f10e..1c3d84663e 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -120,10 +120,10 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) foreach (var methodDefinition in typeDefinition.Methods) { - if (methodDefinition.Name == nameof(NetworkBehaviour.SendServerRpc) - || methodDefinition.Name == nameof(NetworkBehaviour.SendClientRpc)) + if (methodDefinition.Name == nameof(NetworkBehaviour.__sendServerRpc) + || methodDefinition.Name == nameof(NetworkBehaviour.__sendClientRpc)) { - methodDefinition.IsPublic = true; + methodDefinition.IsFamily = true; } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 20b1d692d7..4eea4a0b5b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -35,8 +35,8 @@ internal enum __RpcExecStage #pragma warning restore 414 // restore assigned but its value is never used #pragma warning restore IDE1006 // restore naming rule violation check - - internal void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams rpcParams, RpcDelivery delivery) + // RuntimeAccessModifiersILPP will make this `protected` + internal void __sendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams rpcParams, RpcDelivery delivery) { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; switch (delivery) @@ -78,7 +78,8 @@ internal void SendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, Serve #endif } - internal unsafe void SendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, ClientRpcParams rpcParams, RpcDelivery delivery) + // RuntimeAccessModifiersILPP will make this `protected` + internal unsafe void __sendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, ClientRpcParams rpcParams, RpcDelivery delivery) { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; switch (delivery) From afbff243e8566335f196a1b9ca4ddeca8b00030d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 16:21:26 -0500 Subject: [PATCH 49/58] Fix standards check. --- .../Runtime/Core/NetworkBehaviour.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 4eea4a0b5b..e706b2955f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -35,8 +35,12 @@ internal enum __RpcExecStage #pragma warning restore 414 // restore assigned but its value is never used #pragma warning restore IDE1006 // restore naming rule violation check +#pragma warning disable 414 // disable assigned but its value is never used +#pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `protected` internal void __sendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, ServerRpcParams rpcParams, RpcDelivery delivery) +#pragma warning restore 414 // restore assigned but its value is never used +#pragma warning restore IDE1006 // restore naming rule violation check { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; switch (delivery) @@ -78,8 +82,12 @@ internal void __sendServerRpc(ref FastBufferWriter writer, uint rpcMethodId, Ser #endif } +#pragma warning disable 414 // disable assigned but its value is never used +#pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `protected` internal unsafe void __sendClientRpc(ref FastBufferWriter writer, uint rpcMethodId, ClientRpcParams rpcParams, RpcDelivery delivery) +#pragma warning disable 414 // disable assigned but its value is never used +#pragma warning disable IDE1006 // disable naming rule violation check { NetworkDelivery networkDelivery = NetworkDelivery.Reliable; switch (delivery) From 44ee3bec7d8089cf53887323881b307af2ecc581 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 18:07:06 -0500 Subject: [PATCH 50/58] Fixed an edge case where the temp serialize buffer could be too large to copy into the main one. --- .../Runtime/Messaging/MessagingSystem.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index 799bee2f8d..3af38167bc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -372,7 +372,7 @@ internal unsafe int SendMessage(in TMessageType where TClientIdListType : IReadOnlyList { var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE; - var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, maxSize); + var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(MessageHeader), Allocator.Temp, maxSize - sizeof(MessageHeader)); #pragma warning disable CS0728 // Warns that tmpSerializer may be reassigned within Serialize, but Serialize does not reassign it. using (tmpSerializer) { @@ -403,7 +403,8 @@ internal unsafe int SendMessage(in TMessageType { ref var lastQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1); if (lastQueueItem.NetworkDelivery != delivery || - lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position < tmpSerializer.Length) + lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position + < tmpSerializer.Length + sizeof(MessageHeader)) { sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob, maxSize)); From 36628816f1c223efc6f407d006c60ecfb2e8276a Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 16 Sep 2021 18:59:16 -0500 Subject: [PATCH 51/58] Fixed metrics tests. --- .../Runtime/Messaging/NamedMessageTests.cs | 6 +- .../Runtime/Messaging/UnnamedMessageTests.cs | 6 +- .../Runtime/Metrics/MessagingMetricsTests.cs | 206 ++++++++++++------ .../Metrics/OwnershipChangeMetricsTests.cs | 4 +- .../Metrics/TransportBytesMetricsTests.cs | 47 ++-- 5 files changed, 172 insertions(+), 97 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs index f22ad31e64..a8f0a44aa1 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs @@ -31,7 +31,7 @@ public IEnumerator NamedMessageIsReceivedOnClientWithContent() } ulong receivedMessageSender = 0; - Guid receivedMessageContent; + Guid receivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, (ulong sender, ref FastBufferReader reader) => @@ -63,7 +63,7 @@ public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent() } ulong firstReceivedMessageSender = 0; - Guid firstReceivedMessageContent; + Guid firstReceivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, (ulong sender, ref FastBufferReader reader) => @@ -74,7 +74,7 @@ public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent() }); ulong secondReceivedMessageSender = 0; - Guid secondReceivedMessageContent; + Guid secondReceivedMessageContent = new Guid(); SecondClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, (ulong sender, ref FastBufferReader reader) => diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs index ca0ad1a9dc..3dd6f1e7d8 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs @@ -29,7 +29,7 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() } ulong receivedMessageSender = 0; - Guid receivedMessageContent; + Guid receivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { @@ -58,7 +58,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() } ulong firstReceivedMessageSender = 0; - Guid firstReceivedMessageContent; + Guid firstReceivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { @@ -68,7 +68,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() }; ulong secondReceivedMessageSender = 0; - Guid secondReceivedMessageContent; + Guid secondReceivedMessageContent = new Guid(); SecondClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs index f721a6157c..170601b6f2 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using NUnit.Framework; +using Unity.Collections; using Unity.Multiplayer.Tools.MetricTypes; using Unity.Netcode.RuntimeTests.Metrics.Utlity; using UnityEngine; @@ -15,24 +16,29 @@ namespace Unity.Netcode.RuntimeTests.Metrics { public class MessagingMetricsTests : DualClientMetricTestBase { - const uint MessageNameHashSize = 5; - const uint MessageContentStringLength = 1; + const uint MessageNameHashSize = 8; - const uint MessageOverhead = MessageNameHashSize + MessageContentStringLength; + const uint MessageOverhead = MessageNameHashSize; protected override int NbClients => 2; [UnityTest] public IEnumerator TrackNetworkMessageSentMetric() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent); - Server.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, memoryStream); + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } yield return waitForMetricValues.WaitForMetricsReceived(); @@ -40,50 +46,61 @@ public IEnumerator TrackNetworkMessageSentMetric() Assert.AreEqual(1, networkMessageSentMetricValues.Count); var networkMessageEvent = networkMessageSentMetricValues.First(); - Assert.AreEqual(MessageQueueContainer.GetMessageTypeName(MessageQueueContainer.MessageType.NamedMessage), networkMessageEvent.Name); + Assert.AreEqual(nameof(NamedMessage), networkMessageEvent.Name); Assert.AreEqual(FirstClient.LocalClientId, networkMessageEvent.Connection.Id); } [UnityTest] public IEnumerator TrackNetworkMessageSentMetricToMultipleClients() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent); - - Server.CustomMessagingManager.SendNamedMessage(messageName, new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, memoryStream); + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, ref writer); + } + finally + { + writer.Dispose(); + } + yield return waitForMetricValues.WaitForMetricsReceived(); var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound(); - Assert.AreEqual(2, networkMessageSentMetricValues.Count(x => x.Name.Equals(MessageQueueContainer.GetMessageTypeName(MessageQueueContainer.MessageType.NamedMessage)))); + Assert.AreEqual(2, networkMessageSentMetricValues.Count(x => x.Name.Equals(nameof(NamedMessage)))); } [UnityTest] public IEnumerator TrackNetworkMessageReceivedMetric() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); LogAssert.Expect(LogType.Log, $"Received from {Server.LocalClientId}"); - FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName, (sender, payload) => + FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName.ToString(), (ulong sender, ref FastBufferReader payload) => { Debug.Log($"Received from {sender}"); }); - var waitForMetricValues = new WaitForMetricValues(FirstClientMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageReceived); - Server.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, memoryStream); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } yield return waitForMetricValues.WaitForMetricsReceived(); var networkMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound(); - Assert.AreEqual(1, networkMessageReceivedValues.Count(x => x.Name.Equals(MessageQueueContainer.GetMessageTypeName(MessageQueueContainer.MessageType.NamedMessage)))); + Assert.AreEqual(1, networkMessageReceivedValues.Count(x => x.Name.Equals(nameof(NamedMessage)))); var namedMessageReceived = networkMessageReceivedValues.First(); Assert.AreEqual(Server.LocalClientId, namedMessageReceived.Connection.Id); @@ -92,14 +109,21 @@ public IEnumerator TrackNetworkMessageReceivedMetric() [UnityTest] public IEnumerator TrackNamedMessageSentMetric() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NamedMessageSent); + + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } - Server.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, memoryStream); yield return waitForMetricValues.WaitForMetricsReceived(); @@ -107,47 +131,62 @@ public IEnumerator TrackNamedMessageSentMetric() Assert.AreEqual(1, namedMessageSentMetricValues.Count); var namedMessageSent = namedMessageSentMetricValues.First(); - Assert.AreEqual(messageName, namedMessageSent.Name); + Assert.AreEqual(messageName.ToString(), namedMessageSent.Name); Assert.AreEqual(FirstClient.LocalClientId, namedMessageSent.Connection.Id); - Assert.AreEqual(messageName.Length + MessageOverhead, namedMessageSent.BytesCount); + Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, namedMessageSent.BytesCount); } [UnityTest] public IEnumerator TrackNamedMessageSentMetricToMultipleClients() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NamedMessageSent); - Server.CustomMessagingManager.SendNamedMessage(messageName, new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, memoryStream); + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, ref writer); + } + finally + { + writer.Dispose(); + } + yield return waitForMetricValues.WaitForMetricsReceived(); var namedMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound(); Assert.AreEqual(2, namedMessageSentMetricValues.Count); - Assert.That(namedMessageSentMetricValues.Select(x => x.Name), Has.All.EqualTo(messageName)); - Assert.That(namedMessageSentMetricValues.Select(x => x.BytesCount), Has.All.EqualTo(messageName.Length + MessageOverhead)); + Assert.That(namedMessageSentMetricValues.Select(x => x.Name), Has.All.EqualTo(messageName.ToString())); + Assert.That(namedMessageSentMetricValues.Select(x => x.BytesCount), Has.All.EqualTo(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead)); } [UnityTest] public IEnumerator TrackNamedMessageReceivedMetric() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - + var waitForMetricValues = new WaitForMetricValues(FirstClientMetrics.Dispatcher, NetworkMetricTypes.NamedMessageReceived); + + var messageName = Guid.NewGuid(); + LogAssert.Expect(LogType.Log, $"Received from {Server.LocalClientId}"); - FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName, (sender, payload) => + FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName.ToString(), (ulong sender, ref FastBufferReader payload) => { Debug.Log($"Received from {sender}"); }); - var waitForMetricValues = new WaitForMetricValues(FirstClientMetrics.Dispatcher, NetworkMetricTypes.NamedMessageReceived); - - Server.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, memoryStream); + var writer = new FastBufferWriter(1300, Allocator.Temp); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } + yield return waitForMetricValues.WaitForMetricsReceived(); @@ -155,20 +194,29 @@ public IEnumerator TrackNamedMessageReceivedMetric() Assert.AreEqual(1, namedMessageReceivedValues.Count); var namedMessageReceived = namedMessageReceivedValues.First(); - Assert.AreEqual(messageName, namedMessageReceived.Name); + Assert.AreEqual(messageName.ToString(), namedMessageReceived.Name); Assert.AreEqual(Server.LocalClientId, namedMessageReceived.Connection.Id); - Assert.AreEqual(messageName.Length + MessageOverhead, namedMessageReceived.BytesCount); + Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, namedMessageReceived.BytesCount); } [UnityTest] public IEnumerator TrackUnnamedMessageSentMetric() { - var message = Guid.NewGuid().ToString(); - using var buffer = new NetworkBuffer(); - buffer.Write(Encoding.UTF8.GetBytes(message)); + var message = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); + try + { + writer.WriteValueSafe(message); + + Server.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } + var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageSent); - Server.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, buffer); yield return waitForMetricValues.WaitForMetricsReceived(); @@ -177,24 +225,32 @@ public IEnumerator TrackUnnamedMessageSentMetric() var unnamedMessageSent = unnamedMessageSentMetricValues.First(); Assert.AreEqual(FirstClient.LocalClientId, unnamedMessageSent.Connection.Id); - Assert.AreEqual(message.Length, unnamedMessageSent.BytesCount); + Assert.AreEqual(FastBufferWriter.GetWriteSize(message), unnamedMessageSent.BytesCount); } [UnityTest] public IEnumerator TrackUnnamedMessageSentMetricToMultipleClients() { - var message = Guid.NewGuid().ToString(); - using var buffer = new NetworkBuffer(); - buffer.Write(Encoding.UTF8.GetBytes(message)); - + var message = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageSent); - Server.CustomMessagingManager.SendUnnamedMessage(new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, buffer); + try + { + writer.WriteValueSafe(message); + + Server.CustomMessagingManager.SendUnnamedMessage(new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, ref writer); + } + finally + { + writer.Dispose(); + } + yield return waitForMetricValues.WaitForMetricsReceived(); var unnamedMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound(); Assert.AreEqual(2, unnamedMessageSentMetricValues.Count); - Assert.That(unnamedMessageSentMetricValues.Select(x => x.BytesCount), Has.All.EqualTo(message.Length)); + Assert.That(unnamedMessageSentMetricValues.Select(x => x.BytesCount), Has.All.EqualTo(FastBufferWriter.GetWriteSize(message))); var clientIds = unnamedMessageSentMetricValues.Select(x => x.Connection.Id).ToList(); Assert.Contains(FirstClient.LocalClientId, clientIds); @@ -204,13 +260,19 @@ public IEnumerator TrackUnnamedMessageSentMetricToMultipleClients() [UnityTest] public IEnumerator TrackUnnamedMessageReceivedMetric() { - var message = Guid.NewGuid().ToString(); - using var buffer = new NetworkBuffer(); - buffer.Write(Encoding.UTF8.GetBytes(message)); - + var message = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); var waitForMetricValues = new WaitForMetricValues(FirstClientMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageReceived); - - Server.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, buffer); + try + { + writer.WriteValueSafe(message); + + Server.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } yield return waitForMetricValues.WaitForMetricsReceived(); @@ -219,7 +281,7 @@ public IEnumerator TrackUnnamedMessageReceivedMetric() var unnamedMessageReceived = unnamedMessageReceivedValues.First(); Assert.AreEqual(Server.LocalClientId, unnamedMessageReceived.Connection.Id); - Assert.AreEqual(message.Length, unnamedMessageReceived.BytesCount); + Assert.AreEqual(FastBufferWriter.GetWriteSize(message), unnamedMessageReceived.BytesCount); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs index 3c041f2680..c4d9a3006e 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/OwnershipChangeMetricsTests.cs @@ -58,7 +58,7 @@ public IEnumerator TrackOwnershipChangeSentMetric() var ownershipChangeSent = metricValues.First(); Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId); Assert.AreEqual(Server.LocalClientId, ownershipChangeSent.Connection.Id); - Assert.AreEqual(2, ownershipChangeSent.BytesCount); + Assert.AreEqual(FastBufferWriter.GetWriteSize(), ownershipChangeSent.BytesCount); } [UnityTest] @@ -79,7 +79,7 @@ public IEnumerator TrackOwnershipChangeReceivedMetric() var ownershipChangeReceived = metricValues.First(); Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeReceived.NetworkId.NetworkId); - Assert.AreEqual(2, ownershipChangeReceived.BytesCount); + Assert.AreEqual(FastBufferWriter.GetWriteSize(), ownershipChangeReceived.BytesCount); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/TransportBytesMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/TransportBytesMetricsTests.cs index ee7fa88599..ff7d8d5308 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/TransportBytesMetricsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/TransportBytesMetricsTests.cs @@ -3,6 +3,7 @@ using System.Collections; using System.IO; using NUnit.Framework; +using Unity.Collections; using Unity.Multiplayer.Tools.MetricTypes; using Unity.Multiplayer.Tools.NetStats; using Unity.Netcode.RuntimeTests.Metrics.Utlity; @@ -12,19 +13,24 @@ namespace Unity.Netcode.RuntimeTests.Metrics { public class TransportBytesMetricsTests : SingleClientMetricTestBase { - const long MessageOverhead = 9; + static readonly long MessageOverhead = 8 + FastBufferWriter.GetWriteSize() + FastBufferWriter.GetWriteSize(); [UnityTest] public IEnumerator TrackTotalNumberOfBytesSent() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - - var observer = new TotalBytesObserver(ServerMetrics.Dispatcher, NetworkMetricTypes.TotalBytesSent); - - Server.CustomMessagingManager.SendNamedMessage(messageName, Client.LocalClientId, memoryStream); + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); + var observer = new TotalBytesObserver(ClientMetrics.Dispatcher, NetworkMetricTypes.TotalBytesReceived); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), Client.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } var nbFrames = 0; while (!observer.Found || nbFrames < 10) @@ -34,20 +40,27 @@ public IEnumerator TrackTotalNumberOfBytesSent() } Assert.True(observer.Found); - Assert.AreEqual(messageName.Length + MessageOverhead, observer.Value); + Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, observer.Value); } [UnityTest] public IEnumerator TrackTotalNumberOfBytesReceived() { - var messageName = Guid.NewGuid().ToString(); - using var memoryStream = new MemoryStream(); - using var binaryWriter = new BinaryWriter(memoryStream); - binaryWriter.Write(messageName); - + var messageName = Guid.NewGuid(); + var writer = new FastBufferWriter(1300, Allocator.Temp); var observer = new TotalBytesObserver(ClientMetrics.Dispatcher, NetworkMetricTypes.TotalBytesReceived); + try + { + writer.WriteValueSafe(messageName); + + Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), Client.LocalClientId, ref writer); + } + finally + { + writer.Dispose(); + } + - Server.CustomMessagingManager.SendNamedMessage(messageName, Client.LocalClientId, memoryStream); var nbFrames = 0; while (!observer.Found || nbFrames < 10) @@ -57,7 +70,7 @@ public IEnumerator TrackTotalNumberOfBytesReceived() } Assert.True(observer.Found); - Assert.AreEqual(messageName.Length + MessageOverhead, observer.Value); + Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, observer.Value); } private class TotalBytesObserver : IMetricObserver From 87ea6e40f775191403a185ab5ec60b479c77032d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 10:34:02 -0500 Subject: [PATCH 52/58] standards.py --fix --- .../Tests/Runtime/Messaging/NamedMessageTests.cs | 6 +++--- .../Tests/Runtime/Messaging/UnnamedMessageTests.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs index a8f0a44aa1..cdda0ac5f9 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs @@ -31,7 +31,7 @@ public IEnumerator NamedMessageIsReceivedOnClientWithContent() } ulong receivedMessageSender = 0; - Guid receivedMessageContent = new Guid(); + var receivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, (ulong sender, ref FastBufferReader reader) => @@ -63,7 +63,7 @@ public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent() } ulong firstReceivedMessageSender = 0; - Guid firstReceivedMessageContent = new Guid(); + var firstReceivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, (ulong sender, ref FastBufferReader reader) => @@ -74,7 +74,7 @@ public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent() }); ulong secondReceivedMessageSender = 0; - Guid secondReceivedMessageContent = new Guid(); + var secondReceivedMessageContent = new Guid(); SecondClient.CustomMessagingManager.RegisterNamedMessageHandler( messageName, (ulong sender, ref FastBufferReader reader) => diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs index 3dd6f1e7d8..4e21d86ae9 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs @@ -29,7 +29,7 @@ public IEnumerator UnnamedMessageIsReceivedOnClientWithContent() } ulong receivedMessageSender = 0; - Guid receivedMessageContent = new Guid(); + var receivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { @@ -58,7 +58,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() } ulong firstReceivedMessageSender = 0; - Guid firstReceivedMessageContent = new Guid(); + var firstReceivedMessageContent = new Guid(); FirstClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { @@ -68,7 +68,7 @@ public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent() }; ulong secondReceivedMessageSender = 0; - Guid secondReceivedMessageContent = new Guid(); + var secondReceivedMessageContent = new Guid(); SecondClient.CustomMessagingManager.OnUnnamedMessage += (ulong sender, ref FastBufferReader reader) => { From ab652f5040e6b91c02f13be09a7b84049196fb97 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 10:40:39 -0500 Subject: [PATCH 53/58] Renamed IBufferSerializerImplementation to IReaderWriter --- .../Components/NetworkAnimator.cs | 12 ++++++------ .../Components/NetworkTransform.cs | 2 +- .../Runtime/Serialization/BufferSerializer.cs | 2 +- .../Runtime/Serialization/BufferSerializerReader.cs | 2 +- .../Runtime/Serialization/BufferSerializerWriter.cs | 2 +- .../Runtime/Serialization/INetworkSerializable.cs | 2 +- ...rSerializerImplementation.cs => IReaderWriter.cs} | 0 .../Tests/Runtime/NetworkVariableTests.cs | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) rename com.unity.netcode.gameobjects/Runtime/Serialization/{IBufferSerializerImplementation.cs => IReaderWriter.cs} (100%) diff --git a/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs index eb01597d64..759df5f5e5 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkAnimator.cs @@ -76,7 +76,7 @@ public bool SetTrigger(int key) return TriggerParameters.Add(key); } - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { SerializeIntParameters(serializer); SerializeFloatParameters(serializer); @@ -85,7 +85,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IBuffe SerializeAnimatorLayerStates(serializer); } - private void SerializeAnimatorLayerStates(BufferSerializer serializer) where T : IBufferSerializerImplementation + private void SerializeAnimatorLayerStates(BufferSerializer serializer) where T : IReaderWriter { int layerCount = serializer.IsReader ? 0 : LayerStates.Length; serializer.SerializeValue(ref layerCount); @@ -118,7 +118,7 @@ private void SerializeAnimatorLayerStates(BufferSerializer serializer) whe } } - private void SerializeTriggerParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation + private void SerializeTriggerParameters(BufferSerializer serializer) where T : IReaderWriter { int paramCount = serializer.IsReader ? 0 : TriggerParameters.Count; serializer.SerializeValue(ref paramCount); @@ -141,7 +141,7 @@ private void SerializeTriggerParameters(BufferSerializer serializer) where } } - private void SerializeBoolParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation + private void SerializeBoolParameters(BufferSerializer serializer) where T : IReaderWriter { int paramCount = serializer.IsReader ? 0 : BoolParameters.Count; serializer.SerializeValue(ref paramCount); @@ -167,7 +167,7 @@ private void SerializeBoolParameters(BufferSerializer serializer) where T } } - private void SerializeFloatParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation + private void SerializeFloatParameters(BufferSerializer serializer) where T : IReaderWriter { int paramCount = serializer.IsReader ? 0 : FloatParameters.Count; serializer.SerializeValue(ref paramCount); @@ -193,7 +193,7 @@ private void SerializeFloatParameters(BufferSerializer serializer) where T } } - private void SerializeIntParameters(BufferSerializer serializer) where T : IBufferSerializerImplementation + private void SerializeIntParameters(BufferSerializer serializer) where T : IReaderWriter { int paramCount = serializer.IsReader ? 0 : IntParameters.Count; serializer.SerializeValue(ref paramCount); diff --git a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs index b23991aabe..569a892928 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs @@ -82,7 +82,7 @@ public bool HasScaleZ public float RotAngleX, RotAngleY, RotAngleZ; public float ScaleX, ScaleY, ScaleZ; - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { // InLocalSpace + HasXXX Bits serializer.SerializeValue(ref Bitset); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index 5d2a37b13c..86dfc726d2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -19,7 +19,7 @@ namespace Unity.Netcode /// things to happen because the struct's lifetime could outlive the Reader/Writer's.) /// /// The implementation struct - public ref struct BufferSerializer where TImplementation : IBufferSerializerImplementation + public ref struct BufferSerializer where TImplementation : IReaderWriter { private TImplementation m_Implementation; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index b7633d11b6..d4710309f1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -2,7 +2,7 @@ namespace Unity.Netcode { - internal struct BufferSerializerReader : IBufferSerializerImplementation + internal struct BufferSerializerReader : IReaderWriter { private Ref m_Reader; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 60569225f6..3f9a2e9520 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -2,7 +2,7 @@ namespace Unity.Netcode { - internal struct BufferSerializerWriter : IBufferSerializerImplementation + internal struct BufferSerializerWriter : IReaderWriter { private Ref m_Writer; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs index 54b979b729..731e7e4d28 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/INetworkSerializable.cs @@ -2,6 +2,6 @@ namespace Unity.Netcode { public interface INetworkSerializable { - void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation; + void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs rename to com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs index b7ed5f1737..ac85eda9db 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs @@ -11,7 +11,7 @@ public struct FixedString32Struct : INetworkSerializable { public FixedString32Bytes FixedString; - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref FixedString); } @@ -22,7 +22,7 @@ public struct TestStruct : INetworkSerializable public uint SomeInt; public bool SomeBool; - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref SomeInt); serializer.SerializeValue(ref SomeBool); From 70881132783390e549043f360665c94712f717e2 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 10:46:53 -0500 Subject: [PATCH 54/58] Ok... actually renamed IBufferSerializerImplementation to IReaderWriter... --- .../Runtime/Serialization/BufferSerializer.cs | 8 ++++---- .../Runtime/Serialization/IReaderWriter.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index 86dfc726d2..7b7ae3a9f6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -18,10 +18,10 @@ namespace Unity.Netcode /// requirements. (Allowing direct access to the IBufferSerializerImplementation struct would allow dangerous /// things to happen because the struct's lifetime could outlive the Reader/Writer's.) /// - /// The implementation struct - public ref struct BufferSerializer where TImplementation : IReaderWriter + /// The implementation struct + public ref struct BufferSerializer where TReaderWriter : IReaderWriter { - private TImplementation m_Implementation; + private TReaderWriter m_Implementation; /// /// Check if the contained implementation is a reader @@ -33,7 +33,7 @@ namespace Unity.Netcode /// public bool IsWriter => m_Implementation.IsWriter; - public BufferSerializer(TImplementation implementation) + internal BufferSerializer(TReaderWriter implementation) { m_Implementation = implementation; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs index e94b6a4a12..ce6d6be7c2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs @@ -1,6 +1,6 @@ namespace Unity.Netcode { - public interface IBufferSerializerImplementation + public interface IReaderWriter { bool IsReader { get; } bool IsWriter { get; } From 9c45ef10ac715543da97d850a5d5006575d2117e Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 10:47:07 -0500 Subject: [PATCH 55/58] Missed a meta file --- ...erSerializerImplementation.cs.meta => IReaderWriter.cs.meta} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename com.unity.netcode.gameobjects/Runtime/Serialization/{IBufferSerializerImplementation.cs.meta => IReaderWriter.cs.meta} (83%) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs.meta index f5c741d324..61e9aae96b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IReaderWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9220f80eab4051e4d9b9504ef840eba4 +guid: daea292c2c9ec794fb78018d4530c1d6 MonoImporter: externalObjects: {} serializedVersion: 2 From d095d4abe3a1c6187dc49dccf1a58f63bce09d18 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 10:55:16 -0500 Subject: [PATCH 56/58] Fixed missing OnAfterSendMessage hook when sending to localhost. --- .../Runtime/Messaging/MessagingSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs index 3af38167bc..3d83849573 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs @@ -430,6 +430,10 @@ internal unsafe int SendMessage(in TMessageType SenderId = clientId, Timestamp = Time.realtimeSinceStartup }); + for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) + { + m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(TMessageType), delivery, tmpSerializer.Length + sizeof(MessageHeader)); + } continue; } From 4a5a85ce73df571b9900cd580450013605ee5546 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 11:24:01 -0500 Subject: [PATCH 57/58] Take 3 at a rename. --- .../Runtime/Serialization/BufferSerializer.cs | 4 ++-- testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs | 2 +- .../Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index 7b7ae3a9f6..70b084c334 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -13,9 +13,9 @@ namespace Unity.Netcode /// BufferSerializer doesn't wrapp FastBufferReader or FastBufferWriter directly because it can't. /// ref structs can't implement interfaces, and in order to be able to have two different implementations with /// the same interface (which allows us to avoid an "if(IsReader)" on every call), the thing directly wrapping - /// the struct has to implement an interface. So IBufferSerializerImplementation exists as the interface, + /// the struct has to implement an interface. So IReaderWriter exists as the interface, /// which is implemented by a normal struct, while the ref struct wraps the normal one to enforce the two above - /// requirements. (Allowing direct access to the IBufferSerializerImplementation struct would allow dangerous + /// requirements. (Allowing direct access to the IReaderWriter struct would allow dangerous /// things to happen because the struct's lifetime could outlive the Reader/Writer's.) /// /// The implementation struct diff --git a/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs b/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs index ec95df6703..d50373d1e0 100644 --- a/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs +++ b/testproject/Assets/Tests/Manual/Scripts/StatsInfoContainer.cs @@ -11,7 +11,7 @@ public struct StatsInfoContainer : INetworkSerializable { public List StatValues; - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { if (serializer.IsReader) { diff --git a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs index 89607b8a18..61fc2d7b40 100644 --- a/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs +++ b/testproject/Assets/Tests/Runtime/RpcUserSerializableTypesTest.cs @@ -657,7 +657,7 @@ public class UserSerializableClass : INetworkSerializable public ulong MyulongValue; public List MyByteListValues; - public void NetworkSerialize(BufferSerializer serializer) where T : IBufferSerializerImplementation + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref MyintValue); serializer.SerializeValue(ref MyulongValue); From e1c3b6a2c60618ffff078fea0b888484196f549d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 17 Sep 2021 12:43:33 -0500 Subject: [PATCH 58/58] standards.py --fix --- .../Runtime/Messaging/CustomMessageManager.cs | 6 +++--- .../Runtime/Spawning/NetworkSpawnManager.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs index a9008bb4d9..369b1987f8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs @@ -199,9 +199,9 @@ public void SendNamedMessage(string messageName, ulong clientId, ref FastBufferW Data = messageStream }; var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId); - var bytesReported = m_NetworkManager.LocalClientId == clientId - ? 0 - : size; + var bytesReported = m_NetworkManager.LocalClientId == clientId + ? 0 + : size; m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientId, messageName, size); } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 8fb4702094..3021f67075 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -154,9 +154,9 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId) foreach (var client in NetworkManager.ConnectedClients) { - var bytesReported = NetworkManager.LocalClientId == client.Key - ? 0 - : size; + var bytesReported = NetworkManager.LocalClientId == client.Key + ? 0 + : size; NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject.NetworkObjectId, networkObject.name, bytesReported); } }