diff --git a/CHANGELOG.md b/CHANGELOG.md index a56dc0b..e2e7cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Change log +## [1.3.3] - 2023-03-17 + +### Fixes +* Fixed an issue where calling `ScheduleFlushSend` before the socket was bound would still result in socket system calls being made, resulting in errors being logged. + +## [1.3.2] - 2023-03-09 + +### Fixes +* Fixed issue where UWP Xbox builds were crashing because the split buffer fix was not including UWP defines. +* Fixed an issue where `IPCNetworkInterface` would perform useless work for each packet received. +* Fixed an issue where `ReliableSequencedPipelineStage` could end up duplicating packets when sending reliable packets while the send queue is full. + +## [1.3.1] - 2022-12-09 + +### Changes +* It is now possible to set a window size of up to 64 for `ReliableSequencedPipelineStage` (use `NetworkSettings.WithReliableStageParameters` to modify the value). Doing so increases the packet header size by 4 bytes though, so the default value remains at 32. + +### Fixes +* Fixed an issue where if one end of a reliable pipeline stopped sending any traffic and its latest ACK message was lost, then the other end would stall. +* Fixed a crash when using DTLS if an update was delayed for long enough that both the disconnection and heartbeat timeouts expire. + ## [1.3.0] - 2022-09-27 ### New features diff --git a/Documentation~/filter.yml b/Documentation~/filter.yml index 52d8803..b52219d 100644 --- a/Documentation~/filter.yml +++ b/Documentation~/filter.yml @@ -1,12 +1,10 @@ -# https://dotnet.github.io/docfx/tutorial/howto_filter_out_unwanted_apis_attributes.html - apiRules: - exclude: - uidRegex: .*\.Tests.* - type: Namespace + type: Namespace + uidRegex: .*\.Tests.* - exclude: - uidRegex: .*\.Editor$ - type: Namespace + type: Namespace + uidRegex: .*\.Editor$ - exclude: - uidRegex: .*\.Samples$ - type: Namespace + type: Namespace + uidRegex: .*\.Samples$ diff --git a/Editor/RoslynAnalyzerFix.cs b/Editor/RoslynAnalyzerFix.cs index 842df37..3515711 100644 --- a/Editor/RoslynAnalyzerFix.cs +++ b/Editor/RoslynAnalyzerFix.cs @@ -13,10 +13,15 @@ namespace Unity.Networking.Editor { + /// Asset post-processor to fix loading of the Roslyn analyzer. public class RoslynAnalyzerFix : AssetPostprocessor { private static readonly XNamespace xNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; + /// Fix the inclusion of the Roslyn analyzer in CS project. + /// Path to the project file (unused). + /// Content of the project file. + /// New content of the project file. public static string OnGeneratedCSProject(string path, string content) { // There is currently a bug in both VS Code Editor/Rider that doesn't properly resolve diff --git a/Runtime/BaselibNetworkArray.cs b/Runtime/BaselibNetworkArray.cs index 1e8874d..0429cb6 100644 --- a/Runtime/BaselibNetworkArray.cs +++ b/Runtime/BaselibNetworkArray.cs @@ -1,7 +1,7 @@ -#if UNITY_STANDALONE_WIN || UNITY_GAMECORE || UNITY_XBOXONE || UNITY_EDITOR_WIN - #define BASELIB_USE_SPLIT_BUFFERS +#if UNITY_STANDALONE_WIN || UNITY_GAMECORE || UNITY_XBOXONE || UNITY_EDITOR_WIN || UNITY_WSA || UNITY_WSA_10_0 +#define BASELIB_USE_SPLIT_BUFFERS #else - #undef BASELIB_USE_SPLIT_BUFFERS +#undef BASELIB_USE_SPLIT_BUFFERS #endif using System; diff --git a/Runtime/BaselibNetworkInterface.cs b/Runtime/BaselibNetworkInterface.cs index 5d48694..f5a3765 100644 --- a/Runtime/BaselibNetworkInterface.cs +++ b/Runtime/BaselibNetworkInterface.cs @@ -19,6 +19,7 @@ namespace Unity.Networking.Transport using NetworkEndpoint = Binding.Baselib_RegisteredNetwork_Endpoint; using NetworkSocket = Binding.Baselib_RegisteredNetwork_Socket_UDP; + /// Extensions for . public static class BaselibNetworkParameterExtensions { internal const int k_defaultRxQueueSize = 64; @@ -28,9 +29,11 @@ public static class BaselibNetworkParameterExtensions /// /// Sets the values for the /// + /// to modify. /// /// /// + /// Modified . public static ref NetworkSettings WithBaselibNetworkInterfaceParameters( ref this NetworkSettings settings, int receiveQueueCapacity = k_defaultRxQueueSize, @@ -53,6 +56,7 @@ public static ref NetworkSettings WithBaselibNetworkInterfaceParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static BaselibNetworkParameter GetBaselibNetworkInterfaceParameters(ref this NetworkSettings settings) { @@ -85,6 +89,8 @@ public struct BaselibNetworkParameter : INetworkParameter /// public uint maximumPayloadSize; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() { var valid = true; @@ -253,6 +259,7 @@ internal unsafe struct BaselibData /// Converts a generic to its version for the . /// /// The endpoint to convert. + /// The new . /// returns 0 on success and sets the converted endpoint value public unsafe int CreateInterfaceEndPoint(NetworkEndPoint address, out NetworkInterfaceEndPoint endpoint) { @@ -318,8 +325,8 @@ public unsafe NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoi /// /// Initializes a instance of the struct. /// - /// An array of INetworkParameter. If there is no present, the default values are used. - /// Returns 0 on succees. + /// with which to configure the interface. + /// Returns 0 on success. public unsafe int Initialize(NetworkSettings settings) { configuration = settings.GetBaselibNetworkInterfaceParameters(); @@ -617,7 +624,12 @@ public unsafe int Bind(NetworkInterfaceEndPoint endpoint) checked((uint)configuration.receiveQueueCapacity), &error); if (error.code != ErrorCode.Success) + { + if (error.code == ErrorCode.AddressInUse) + UnityEngine.Debug.LogError("Failed to bind the socket because address is already in use. " + + "It is likely that another process is already listening on the same port."); return (int)error.code == -1 ? (int)Error.StatusCode.NetworkSocketError : -(int)error.code; + } // Close old socket now that new one has been successfully created. if (m_Baselib[0].m_Socket.handle != IntPtr.Zero) @@ -709,6 +721,7 @@ private static unsafe int BeginSendMessage(out NetworkInterfaceSendHandle handle var baselib = (BaselibData*)userData; handle = default; int index = baselib->m_PayloadsTx.AcquireHandle(); + if (index < 0) return (int)Error.StatusCode.NetworkSendQueueFull; @@ -747,7 +760,7 @@ private static unsafe int EndSendMessage(ref NetworkInterfaceSendHandle handle, messagePtr, 1u, &error); - if (error.code != ErrorCode.Success) + if (error.code != ErrorCode.Success || count != 1u) { baselib->m_PayloadsTx.ReleaseHandle(index); return (int)error.code == -1 ? -1 : -(int)error.code; diff --git a/Runtime/DataStream.cs b/Runtime/DataStream.cs index 408f42f..63d9884 100644 --- a/Runtime/DataStream.cs +++ b/Runtime/DataStream.cs @@ -25,10 +25,10 @@ internal struct UIntFloat /// to serialize data for sending and then to deserialize when receiving. /// /// - /// The reader can be used to deserialize the data from a NativeArray, writing data - /// to a NativeArray and reading it back can be done like this: + /// The reader can be used to deserialize the data from a NativeArray<byte>, writing data + /// to a NativeArray<byte> and reading it back can be done like this: /// - /// using (var data = new NativeArray(16, Allocator.Persistent)) + /// using (var data = new NativeArray<byte>(16, Allocator.Persistent)) /// { /// var dataWriter = new DataStreamWriter(data); /// dataWriter.WriteInt(42); @@ -47,7 +47,7 @@ internal struct UIntFloat /// the end when you know the value. /// /// - /// using (var data = new NativeArray(16, Allocator.Persistent)) + /// using (var data = new NativeArray<byte>(16, Allocator.Persistent)) /// { /// var dataWriter = new DataStreamWriter(data); /// // My header data @@ -71,6 +71,16 @@ internal struct UIntFloat [StructLayout(LayoutKind.Sequential)] public unsafe struct DataStreamWriter { + /// + /// Show the byte order in which the current computer architecture stores data. + /// + /// + /// Different computer architectures store data using different byte orders. + /// + /// Big-endian: the most significant byte is at the left end of a word. + /// Little-endian: means the most significant byte is at the right end of a word. + /// + /// public static bool IsLittleEndian { get @@ -132,6 +142,10 @@ public DataStreamWriter(byte* data, int length) Initialize(out this, na); } + /// + /// Convert internal data buffer to NativeArray for use in entities APIs. + /// + /// NativeArray representation of internal buffer. public NativeArray AsNativeArray() { var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(m_Data.buffer, Length, Allocator.Invalid); @@ -234,6 +248,10 @@ private void SyncBitData() } } + /// + /// Causes any buffered bits to be written to the data buffer. + /// Note this needs to be invoked after using methods that writes directly to the bit buffer. + /// public void Flush() { while (m_Data.bitIndex > 0) @@ -246,6 +264,12 @@ public void Flush() m_Data.bitIndex = 0; } + /// + /// Writes a byte array (as pointer and length) to the stream. + /// + /// Pointer to the array. + /// Length of the array. + /// Whether the write was successful public bool WriteBytes(byte* data, int bytes) { CheckWrite(); @@ -261,6 +285,11 @@ public bool WriteBytes(byte* data, int bytes) return true; } + /// + /// Writes an unsigned byte to the current stream and advances the stream position by one byte. + /// + /// The unsigned byte to write. + /// Whether the write was successful public bool WriteByte(byte value) { return WriteBytes((byte*)&value, sizeof(byte)); @@ -270,63 +299,123 @@ public bool WriteByte(byte value) /// Copy NativeArray of bytes into the writers data buffer. /// /// Source byte array + /// Whether the write was successful public bool WriteBytes(NativeArray value) { return WriteBytes((byte*)value.GetUnsafeReadOnlyPtr(), value.Length); } + /// + /// Writes a 2-byte signed short to the current stream and advances the stream position by two bytes. + /// + /// The 2-byte signed short to write. + /// Whether the write was successful public bool WriteShort(short value) { return WriteBytes((byte*)&value, sizeof(short)); } + /// + /// Writes a 2-byte unsigned short to the current stream and advances the stream position by two bytes. + /// + /// The 2-byte unsigned short to write. + /// Whether the write was successful public bool WriteUShort(ushort value) { return WriteBytes((byte*)&value, sizeof(ushort)); } + /// + /// Writes a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes. + /// + /// The 4-byte signed integer to write. + /// Whether the write was successful public bool WriteInt(int value) { return WriteBytes((byte*)&value, sizeof(int)); } + /// + /// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes. + /// + /// The 4-byte unsigned integer to write. + /// Whether the write was successful public bool WriteUInt(uint value) { return WriteBytes((byte*)&value, sizeof(uint)); } + /// + /// Writes an 8-byte signed long from the stream and advances the current position of the stream by eight bytes. + /// + /// The 8-byte signed long to write. + /// Whether the write was successful public bool WriteLong(long value) { return WriteBytes((byte*)&value, sizeof(long)); } + /// + /// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes. + /// + /// The 8-byte unsigned long to write. + /// Whether the write was successful public bool WriteULong(ulong value) { return WriteBytes((byte*)&value, sizeof(ulong)); } + /// + /// Writes a 2-byte signed short to the current stream using Big-endian byte order and advances the stream position by two bytes. + /// If the stream is in little-endian order, the byte order will be swapped. + /// + /// The 2-byte signed short to write. + /// Whether the write was successful public bool WriteShortNetworkByteOrder(short value) { short netValue = IsLittleEndian ? ByteSwap(value) : value; return WriteBytes((byte*)&netValue, sizeof(short)); } + /// + /// Writes a 2-byte unsigned short to the current stream using Big-endian byte order and advances the stream position by two bytes. + /// If the stream is in little-endian order, the byte order will be swapped. + /// + /// The 2-byte unsigned short to write. + /// Whether the write was successful public bool WriteUShortNetworkByteOrder(ushort value) { return WriteShortNetworkByteOrder((short)value); } + /// + /// Writes a 4-byte signed integer from the current stream using Big-endian byte order and advances the current position of the stream by four bytes. + /// If the current machine is in little-endian order, the byte order will be swapped. + /// + /// The 4-byte signed integer to write. + /// Whether the write was successful public bool WriteIntNetworkByteOrder(int value) { int netValue = IsLittleEndian ? ByteSwap(value) : value; return WriteBytes((byte*)&netValue, sizeof(int)); } + /// + /// Writes a 4-byte unsigned integer from the current stream using Big-endian byte order and advances the current position of the stream by four bytes. + /// If the stream is in little-endian order, the byte order will be swapped. + /// + /// The 4-byte unsigned integer to write. + /// Whether the write was successful public bool WriteUIntNetworkByteOrder(uint value) { return WriteIntNetworkByteOrder((int)value); } + /// + /// Writes a 4-byte floating point value to the data stream. + /// + /// The 4-byte floating point value to write. + /// Whether the write was successful public bool WriteFloat(float value) { UIntFloat uf = new UIntFloat(); @@ -352,6 +441,12 @@ void WriteRawBitsInternal(uint value, int numbits) m_Data.bitIndex += numbits; } + /// + /// Appends a specified number of bits to the data stream. + /// + /// The bits to write. + /// A positive number of bytes to write. + /// Whether the write was successful public bool WriteRawBits(uint value, int numbits) { CheckWrite(); @@ -366,6 +461,12 @@ public bool WriteRawBits(uint value, int numbits) return true; } + /// + /// Writes a 4-byte unsigned integer value to the data stream using a . + /// + /// The 4-byte unsigned integer to write. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedUInt(uint value, NetworkCompressionModel model) { CheckWrite(); @@ -385,53 +486,117 @@ public bool WritePackedUInt(uint value, NetworkCompressionModel model) return true; } + /// + /// Writes an 8-byte unsigned long value to the data stream using a . + /// + /// The 8-byte unsigned long to write. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedULong(ulong value, NetworkCompressionModel model) { return WritePackedUInt((uint)(value >> 32), model) & WritePackedUInt((uint)(value & 0xFFFFFFFF), model); } + /// + /// Writes a 4-byte signed integer value to the data stream using a . + /// Negative values are interleaved between positive values, i.e. (0, -1, 1, -2, 2) + /// + /// The 4-byte signed integer to write. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedInt(int value, NetworkCompressionModel model) { uint interleaved = (uint)((value >> 31) ^ (value << 1)); // interleave negative values between positive values: 0, -1, 1, -2, 2 return WritePackedUInt(interleaved, model); } + /// + /// Writes a 8-byte signed long value to the data stream using a . + /// + /// The 8-byte signed long to write. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedLong(long value, NetworkCompressionModel model) { ulong interleaved = (ulong)((value >> 63) ^ (value << 1)); // interleave negative values between positive values: 0, -1, 1, -2, 2 return WritePackedULong(interleaved, model); } + /// + /// Writes a 4-byte floating point value to the data stream using a . + /// + /// The 4-byte floating point value to write. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedFloat(float value, NetworkCompressionModel model) { return WritePackedFloatDelta(value, 0, model); } + /// + /// Writes a delta 4-byte unsigned integer value to the data stream using a . + /// Note that the Uint values are cast to an Int after computing the diff. + /// + /// The current 4-byte unsigned integer value. + /// The previous 4-byte unsigned integer value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedUIntDelta(uint value, uint baseline, NetworkCompressionModel model) { int diff = (int)(baseline - value); return WritePackedInt(diff, model); } + /// + /// Writes a delta 4-byte signed integer value to the data stream using a . + /// + /// The current 4-byte signed integer value. + /// The previous 4-byte signed integer value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedIntDelta(int value, int baseline, NetworkCompressionModel model) { int diff = (int)(baseline - value); return WritePackedInt(diff, model); } + /// + /// Writes a delta 8-byte signed long value to the data stream using a . + /// + /// The current 8-byte signed long value. + /// The previous 8-byte signed long value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedLongDelta(long value, long baseline, NetworkCompressionModel model) { long diff = (long)(baseline - value); return WritePackedLong(diff, model); } + /// + /// Writes a delta 8-byte unsigned long value to the data stream using a . + /// Note that the unsigned long values are cast to a signed long after computing the diff. + /// + /// The current 8-byte unsigned long value. + /// The previous 8-byte unsigned long, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public bool WritePackedULongDelta(ulong value, ulong baseline, NetworkCompressionModel model) { long diff = (long)(baseline - value); return WritePackedLong(diff, model); } + /// + /// Writes a 4-byte floating point value to the data stream. + /// If the data did not change a zero bit is prepended, otherwise a 1 bit is prepended. + /// When reading back the data, the first bit is then checked for whether the data was changed or not. + /// + /// The current 4-byte floating point value. + /// The previous 4-byte floating value, used to compute the diff. + /// Not currently used. + /// Whether the write was successful public bool WritePackedFloatDelta(float value, float baseline, NetworkCompressionModel model) { CheckWrite(); @@ -456,30 +621,59 @@ public bool WritePackedFloatDelta(float value, float baseline, NetworkCompressio return true; } + /// + /// Writes a FixedString32Bytes value to the data stream. + /// + /// The FixedString32Bytes to write. + /// Whether the write was successful public unsafe bool WriteFixedString32(FixedString32Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytes(data, length); } + + /// + /// Writes a FixedString64Bytes value to the data stream. + /// + /// The FixedString64Bytes to write. + /// Whether the write was successful public unsafe bool WriteFixedString64(FixedString64Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytes(data, length); } + + /// + /// Writes a FixedString128Bytes value to the data stream. + /// + /// The FixedString128Bytes to write. + /// Whether the write was successful public unsafe bool WriteFixedString128(FixedString128Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytes(data, length); } + + /// + /// Writes a FixedString512Bytes value to the data stream. + /// + /// The FixedString512Bytes to write. + /// Whether the write was successful public unsafe bool WriteFixedString512(FixedString512Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytes(data, length); } + + /// + /// Writes a FixedString4096Bytes value to the data stream. + /// + /// The FixedString4096Bytes to write. + /// Whether the write was successful public unsafe bool WriteFixedString4096(FixedString4096Bytes str) { int length = (int)*((ushort*)&str) + 2; @@ -487,30 +681,69 @@ public unsafe bool WriteFixedString4096(FixedString4096Bytes str) return WriteBytes(data, length); } + /// + /// Writes a FixedString32Bytes delta value to the data stream using a . + /// + /// The current FixedString32Bytes value. + /// The previous FixedString32Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public unsafe bool WritePackedFixedString32Delta(FixedString32Bytes str, FixedString32Bytes baseline, NetworkCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); } + + /// + /// Writes a delta FixedString64Bytes value to the data stream using a . + /// + /// The current FixedString64Bytes value. + /// The previous FixedString64Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public unsafe bool WritePackedFixedString64Delta(FixedString64Bytes str, FixedString64Bytes baseline, NetworkCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); } + + /// + /// Writes a delta FixedString128Bytes value to the data stream using a . + /// + /// The current FixedString128Bytes value. + /// The previous FixedString128Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public unsafe bool WritePackedFixedString128Delta(FixedString128Bytes str, FixedString128Bytes baseline, NetworkCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); } + + /// + /// Writes a delta FixedString512Bytes value to the data stream using a . + /// + /// The current FixedString512Bytes value. + /// The previous FixedString512Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public unsafe bool WritePackedFixedString512Delta(FixedString512Bytes str, FixedString512Bytes baseline, NetworkCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); } + + /// + /// Writes a delta FixedString4096Bytes value to the data stream using a . + /// + /// The current FixedString4096Bytes value. + /// The previous FixedString4096Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// Whether the write was successful public unsafe bool WritePackedFixedString4096Delta(FixedString4096Bytes str, FixedString4096Bytes baseline, NetworkCompressionModel model) { ushort length = *((ushort*)&str); @@ -634,11 +867,20 @@ struct Context AtomicSafetyHandle m_Safety; #endif + /// + /// Initializes a new instance of the DataStreamReader struct with a NativeArray<byte>. + /// + /// The buffer to attach to the DataStreamReader. public DataStreamReader(NativeArray array) { Initialize(out this, array); } + /// + /// Initializes a new instance of the DataStreamReader struct with a pointer and length. + /// + /// Pointer to the buffer to attach to the DataStreamReader. + /// Length of the buffer to attach to the DataStreamReader. public DataStreamReader(byte* data, int length) { var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(data, length, Allocator.Invalid); @@ -658,6 +900,16 @@ private static void Initialize(out DataStreamReader self, NativeArray arra self.m_Context = default; } + /// + /// Show the byte order in which the current computer architecture stores data. + /// + /// + /// Different computer architectures store data using different byte orders. + /// + /// Big-endian: the most significant byte is at the left end of a word. + /// Little-endian: means the most significant byte is at the right end of a word. + /// + /// public bool IsLittleEndian => DataStreamWriter.IsLittleEndian; private static short ByteSwap(short val) @@ -670,7 +922,11 @@ private static int ByteSwap(int val) return (int)(((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val >> 8) & 0xff00) | ((val >> 24) & 0xff)); } + /// + /// If there is a read failure this returns true. A read failure might happen if this attempts to read more than there is capacity for. + /// public bool HasFailedReads => m_Context.m_FailedReads > 0; + /// /// The total size of the buffer space this reader is working with. /// @@ -693,14 +949,10 @@ public bool IsCreated } /// - /// Read and copy data to the memory location pointed to, an exception will - /// be thrown if it does not fit. + /// Read the requested number of bytes to the given pointer. /// - /// - /// - /// Thrown if the length - /// will put the reader out of bounds based on the current read pointer - /// position. + /// Pointer to write the data to. + /// Number of bytes to read. public void ReadBytes(byte* data, int length) { CheckRead(); @@ -722,25 +974,40 @@ public void ReadBytes(byte* data, int length) } /// - /// Read and copy data into the given NativeArray of bytes, an exception will - /// be thrown if not enough bytes are available. + /// Read and copy data into the given NativeArray of bytes. The number of bytes + /// read is the length of the provided array. /// - /// + /// Array to read the data into. public void ReadBytes(NativeArray array) { ReadBytes((byte*)array.GetUnsafePtr(), array.Length); } + /// + /// Gets the number of bytes read from the data stream. + /// + /// Number of bytes read. public int GetBytesRead() { return m_Context.m_ReadByteIndex - (m_Context.m_BitIndex >> 3); } + /// + /// Gets the number of bits read from the data stream. + /// + /// Number of bits read. public int GetBitsRead() { return (m_Context.m_ReadByteIndex << 3) - m_Context.m_BitIndex; } + /// + /// Sets the current position of this stream to the given value. + /// An error will be logged if is outside the length of the stream. + ///
+ /// In addition this will reset the bit index and the bit buffer. + ///
+ /// Seek position. public void SeekSet(int pos) { if (pos > m_Length) @@ -756,6 +1023,10 @@ public void SeekSet(int pos) m_Context.m_BitBuffer = 0UL; } + /// + /// Reads an unsigned byte from the current stream and advances the current position of the stream by one byte. + /// + /// The next byte read from the current stream, or 0 if the end of the stream has been reached. public byte ReadByte() { byte data; @@ -763,6 +1034,10 @@ public byte ReadByte() return data; } + /// + /// Reads a 2-byte signed short from the current stream and advances the current position of the stream by two bytes. + /// + /// A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached. public short ReadShort() { short data; @@ -770,6 +1045,10 @@ public short ReadShort() return data; } + /// + /// Reads a 2-byte unsigned short from the current stream and advances the current position of the stream by two bytes. + /// + /// A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached. public ushort ReadUShort() { ushort data; @@ -777,6 +1056,10 @@ public ushort ReadUShort() return data; } + /// + /// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes. + /// + /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached. public int ReadInt() { int data; @@ -784,6 +1067,10 @@ public int ReadInt() return data; } + /// + /// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes. + /// + /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached. public uint ReadUInt() { uint data; @@ -791,13 +1078,21 @@ public uint ReadUInt() return data; } + /// + /// Reads an 8-byte signed long from the stream and advances the current position of the stream by eight bytes. + /// + /// An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached. public long ReadLong() { long data; ReadBytes((byte*)&data, sizeof(long)); return data; } - + + /// + /// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes. + /// + /// An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached. public ulong ReadULong() { ulong data; @@ -805,6 +1100,11 @@ public ulong ReadULong() return data; } + /// + /// Reads a 2-byte signed short from the current stream in Big-endian byte order and advances the current position of the stream by two bytes. + /// If the current endianness is in little-endian order, the byte order will be swapped. + /// + /// A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached. public short ReadShortNetworkByteOrder() { short data; @@ -812,11 +1112,21 @@ public short ReadShortNetworkByteOrder() return IsLittleEndian ? ByteSwap(data) : data; } + /// + /// Reads a 2-byte unsigned short from the current stream in Big-endian byte order and advances the current position of the stream by two bytes. + /// If the current endianness is in little-endian order, the byte order will be swapped. + /// + /// A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached. public ushort ReadUShortNetworkByteOrder() { return (ushort)ReadShortNetworkByteOrder(); } + /// + /// Reads a 4-byte signed integer from the current stream in Big-endian byte order and advances the current position of the stream by four bytes. + /// If the current endianness is in little-endian order, the byte order will be swapped. + /// + /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached. public int ReadIntNetworkByteOrder() { int data; @@ -824,11 +1134,20 @@ public int ReadIntNetworkByteOrder() return IsLittleEndian ? ByteSwap(data) : data; } + /// + /// Reads a 4-byte unsigned integer from the current stream in Big-endian byte order and advances the current position of the stream by four bytes. + /// If the current endianness is in little-endian order, the byte order will be swapped. + /// + /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached. public uint ReadUIntNetworkByteOrder() { return (uint)ReadIntNetworkByteOrder(); } + /// + /// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes. + /// + /// A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached. public float ReadFloat() { UIntFloat uf = new UIntFloat(); @@ -836,6 +1155,11 @@ public float ReadFloat() return uf.floatValue; } + /// + /// Reads a 4-byte unsigned integer from the current stream using a and advances the current position the number of bits depending on the model. + /// + /// model for reading value in a packed manner. + /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached. public uint ReadPackedUInt(NetworkCompressionModel model) { CheckRead(); @@ -890,6 +1214,11 @@ uint ReadRawBitsInternal(int numbits) return res; } + /// + /// Reads a specified number of bits from the data stream. + /// + /// A positive number of bytes to write. + /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached. public uint ReadRawBits(int numbits) { CheckRead(); @@ -897,6 +1226,11 @@ public uint ReadRawBits(int numbits) return ReadRawBitsInternal(numbits); } + /// + /// Reads an 8-byte unsigned long value from the data stream using a . + /// + /// model for reading value in a packed manner. + /// An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached. public ulong ReadPackedULong(NetworkCompressionModel model) { //hi @@ -906,47 +1240,112 @@ public ulong ReadPackedULong(NetworkCompressionModel model) return hi; } + /// + /// Reads a 4-byte signed integer value from the data stream using a . + ///
+ /// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2) + ///
+ /// model for reading value in a packed manner. + /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached. public int ReadPackedInt(NetworkCompressionModel model) { uint folded = ReadPackedUInt(model); return (int)(folded >> 1) ^ -(int)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...] } + /// + /// Reads an 8-byte signed long value from the data stream using a . + ///
+ /// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2) + ///
+ /// model for reading value in a packed manner. + /// An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached. public long ReadPackedLong(NetworkCompressionModel model) { ulong folded = ReadPackedULong(model); return (long)(folded >> 1) ^ -(long)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...] } + /// + /// Reads a 4-byte floating point value from the data stream using a . + /// + /// model for reading value in a packed manner. + /// A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached. public float ReadPackedFloat(NetworkCompressionModel model) { return ReadPackedFloatDelta(0, model); } + /// + /// Reads a 4-byte signed integer delta value from the data stream using a . + /// + /// The previous 4-byte signed integer value, used to compute the diff. + /// model for reading value in a packed manner. + /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached. + /// If the data did not change, this also returns 0. + ///
+ /// See: to verify if the read failed.
public int ReadPackedIntDelta(int baseline, NetworkCompressionModel model) { int delta = ReadPackedInt(model); return baseline - delta; } + /// + /// Reads a 4-byte unsigned integer delta value from the data stream using a . + /// + /// The previous 4-byte unsigned integer value, used to compute the diff. + /// model for reading value in a packed manner. + /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached. + /// If the data did not change, this also returns 0. + ///
+ /// See: to verify if the read failed.
public uint ReadPackedUIntDelta(uint baseline, NetworkCompressionModel model) { uint delta = (uint)ReadPackedInt(model); return baseline - delta; } + /// + /// Reads an 8-byte signed long delta value from the data stream using a . + /// + /// The previous 8-byte signed long value, used to compute the diff. + /// model for reading value in a packed manner. + /// An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached. + /// If the data did not change, this also returns 0. + ///
+ /// See: to verify if the read failed.
public long ReadPackedLongDelta(long baseline, NetworkCompressionModel model) { long delta = ReadPackedLong(model); return baseline - delta; } + /// + /// Reads an 8-byte unsigned long delta value from the data stream using a . + /// + /// The previous 8-byte unsigned long value, used to compute the diff. + /// model for reading value in a packed manner. + /// An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached. + /// If the data did not change, this also returns 0. + ///
+ /// See: to verify if the read failed.
public ulong ReadPackedULongDelta(ulong baseline, NetworkCompressionModel model) { ulong delta = (ulong)ReadPackedLong(model); return baseline - delta; } + /// + /// Reads a 4-byte floating point value from the data stream. + /// + /// If the first bit is 0, the data did not change and will be returned. + /// + /// The previous 4-byte floating point value. + /// Not currently used. + /// A 4-byte floating point value read from the current stream, or if there are no changes to the value. + ///
+ /// See: to verify if the read failed.
public float ReadPackedFloatDelta(float baseline, NetworkCompressionModel model) { CheckRead(); @@ -960,6 +1359,10 @@ public float ReadPackedFloatDelta(float baseline, NetworkCompressionModel model) return uf.floatValue; } + /// + /// Reads a FixedString32Bytes value from the current stream and advances the current position of the stream by the length of the string. + /// + /// A FixedString32Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString32Bytes ReadFixedString32() { FixedString32Bytes str; @@ -967,6 +1370,11 @@ public unsafe FixedString32Bytes ReadFixedString32() *(ushort*)&str = ReadFixedString(data, str.Capacity); return str; } + + /// + /// Reads a FixedString64Bytes value from the current stream and advances the current position of the stream by the length of the string. + /// + /// A FixedString64Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString64Bytes ReadFixedString64() { FixedString64Bytes str; @@ -974,6 +1382,11 @@ public unsafe FixedString64Bytes ReadFixedString64() *(ushort*)&str = ReadFixedString(data, str.Capacity); return str; } + + /// + /// Reads a FixedString128Bytes value from the current stream and advances the current position of the stream by the length of the string. + /// + /// A FixedString128Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString128Bytes ReadFixedString128() { FixedString128Bytes str; @@ -981,6 +1394,11 @@ public unsafe FixedString128Bytes ReadFixedString128() *(ushort*)&str = ReadFixedString(data, str.Capacity); return str; } + + /// + /// Reads a FixedString512Bytes value from the current stream and advances the current position of the stream by the length of the string. + /// + /// A FixedString512Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString512Bytes ReadFixedString512() { FixedString512Bytes str; @@ -988,6 +1406,11 @@ public unsafe FixedString512Bytes ReadFixedString512() *(ushort*)&str = ReadFixedString(data, str.Capacity); return str; } + + /// + /// Reads a FixedString4096Bytes value from the current stream and advances the current position of the stream by the length of the string. + /// + /// A FixedString4096Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString4096Bytes ReadFixedString4096() { FixedString4096Bytes str; @@ -996,6 +1419,12 @@ public unsafe FixedString4096Bytes ReadFixedString4096() return str; } + /// + /// Read and copy a fixed string (of unknown length) into the given buffer. + /// + /// Pointer to the buffer to write the string bytes to. + /// Length of the buffer to write the string bytes to. + /// Length of data read into byte array, or zero if error occurred. public unsafe ushort ReadFixedString(byte* data, int maxLength) { ushort length = ReadUShort(); @@ -1010,6 +1439,12 @@ public unsafe ushort ReadFixedString(byte* data, int maxLength) return length; } + /// + /// Reads a FixedString32Bytes delta value to the data stream using a . + /// + /// The previous FixedString32Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// A FixedString32Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes baseline, NetworkCompressionModel model) { FixedString32Bytes str; @@ -1017,6 +1452,13 @@ public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes *(ushort*)&str = ReadPackedFixedStringDelta(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); return str; } + + /// + /// Reads a FixedString64Bytes delta value to the data stream using a . + /// + /// The previous FixedString64Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// A FixedString64Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes baseline, NetworkCompressionModel model) { FixedString64Bytes str; @@ -1024,6 +1466,13 @@ public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes *(ushort*)&str = ReadPackedFixedStringDelta(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); return str; } + + /// + /// Reads a FixedString128Bytes delta value to the data stream using a . + /// + /// The previous FixedString128Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// A FixedString128Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128Bytes baseline, NetworkCompressionModel model) { FixedString128Bytes str; @@ -1031,6 +1480,13 @@ public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128By *(ushort*)&str = ReadPackedFixedStringDelta(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); return str; } + + /// + /// Reads a FixedString512Bytes delta value to the data stream using a . + /// + /// The previous FixedString512Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// A FixedString512Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512Bytes baseline, NetworkCompressionModel model) { FixedString512Bytes str; @@ -1038,6 +1494,13 @@ public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512By *(ushort*)&str = ReadPackedFixedStringDelta(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); return str; } + + /// + /// Reads a FixedString4096Bytes delta value to the data stream using a . + /// + /// The previous FixedString4096Bytes value, used to compute the diff. + /// model for writing value in a packed manner. + /// A FixedString4096Bytes value read from the current stream, or 0 if the end of the stream has been reached. public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString4096Bytes baseline, NetworkCompressionModel model) { FixedString4096Bytes str; @@ -1046,6 +1509,15 @@ public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString409 return str; } + /// + /// Read and copy a fixed string delta (of unknown length) into the given buffer. + /// + /// Pointer to the buffer to write the string bytes to. + /// Length of the buffer to write the string bytes to. + /// Pointer to the previous value, used to compute the diff. + /// Length of the previous value, used to compute the diff. + /// model for writing value in a packed manner. + /// Length of data read into byte array, or zero if error occurred. public unsafe ushort ReadPackedFixedStringDelta(byte* data, int maxLength, byte* baseData, ushort baseLength, NetworkCompressionModel model) { uint length = ReadPackedUIntDelta(baseLength, model); diff --git a/Runtime/INetworkInterface.cs b/Runtime/INetworkInterface.cs index 4fcad57..3daad8e 100644 --- a/Runtime/INetworkInterface.cs +++ b/Runtime/INetworkInterface.cs @@ -83,6 +83,8 @@ public bool AppendPacket(IntPtr data, ref NetworkInterfaceEndPoint address, int /// Check if an address is currently associated with a valid connection. /// This is mostly useful to keep interface internal lists of connections in sync with the correct state. /// + /// Address to check. + /// Whether the address is used or not. public bool IsAddressUsed(NetworkInterfaceEndPoint address) { return m_Driver.IsAddressUsed(address); @@ -216,8 +218,8 @@ public interface INetworkInterface : IDisposable /// /// Initializes the interfacing passing in optional /// - /// The param - /// The int + /// with which to configure the interface. + /// 0 if initialization was successful, negative value otherwise. int Initialize(NetworkSettings settings); /// diff --git a/Runtime/IPCNetworkInterface.cs b/Runtime/IPCNetworkInterface.cs index e28ee4d..8b6ae5a 100644 --- a/Runtime/IPCNetworkInterface.cs +++ b/Runtime/IPCNetworkInterface.cs @@ -62,8 +62,8 @@ public NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoint) /// /// Initializes the interface passing in optional /// - /// The param - /// The status code of the result, 0 being a success. + /// with which to configure the interface. + /// Returns 0 on success. public int Initialize(NetworkSettings settings) { IPCManager.Instance.AddRef(); @@ -128,7 +128,7 @@ public unsafe void Execute() return; } - var resultAppend = receiver.AppendPacket(ptr, ref endpoint, resultReceive); + var resultAppend = receiver.AppendPacket(ptr, ref endpoint, resultReceive, NetworkPacketReceiver.AppendPacketMode.NoCopyNeeded); if (resultAppend == false) return; } diff --git a/Runtime/NetworkConnection.cs b/Runtime/NetworkConnection.cs index 331f882..c8b10c4 100644 --- a/Runtime/NetworkConnection.cs +++ b/Runtime/NetworkConnection.cs @@ -7,9 +7,9 @@ namespace Error { /// Reason for a disconnection event. /// - /// One of these values may be present as a single byte in the - /// obtained with if the event type is - /// . + /// One of these values may be present as a single byte in the + /// obtained with if the event type is + /// . /// public enum DisconnectReason : byte { @@ -17,12 +17,12 @@ public enum DisconnectReason : byte /// Used internally when no other reason fits. /// - /// This shouldn't normally appear as a result of calling . + /// This shouldn't normally appear as a result of calling . /// Default, /// - /// Indicates the connection timed out (see ). + /// Indicates the connection timed out (see ). /// Timeout, @@ -34,7 +34,7 @@ public enum DisconnectReason : byte /// /// Indicates the connection was closed normally by the remote peer after calling - /// or . + /// or . /// ClosedByRemote, @@ -84,7 +84,7 @@ public enum StatusCode /// /// Public representation of a connection. Holds all information needed by the - /// to link it to an internal virtual connection. + /// to link it to an internal virtual connection. /// public struct NetworkConnection : IEquatable { @@ -107,7 +107,7 @@ public enum State /// /// Disconnects a connection and marks it for deletion. The connection will be removed on - /// the next frame. Same as . + /// the next frame. Same as . /// /// Driver to which the connection belongs. /// An Error.StatusCode value (0 on success, -1 otherwise). @@ -122,10 +122,10 @@ public int Disconnect(NetworkDriver driver) /// /// Driver to which the connection belongs. /// - /// A , that will only be populated if a - /// (or possibly ) event was received. + /// A , that will only be populated if a + /// (or possibly ) event was received. /// - /// The of the event. + /// The of the event. public NetworkEvent.Type PopEvent(NetworkDriver driver, out DataStreamReader stream) { return driver.PopEventForConnection(this, out stream); @@ -152,7 +152,7 @@ public NetworkEvent.Type PopEvent(NetworkDriver driver, out DataStreamReader str /// /// Disconnects a connection and marks it for deletion. The connection will be removed on - /// the next frame. Same as . + /// the next frame. Same as . /// /// Driver to which the connection belongs. /// An Error.StatusCode value (0 on success, -1 otherwise). @@ -166,7 +166,7 @@ public int Close(NetworkDriver driver) /// Checks to see if a connection is created. /// /// Connections are considered as created only if they've been obtained by a call to - /// or . + /// or . /// /// Whether the connection is created or not. public bool IsCreated @@ -176,7 +176,7 @@ public bool IsCreated /// Gets the state of the connection. /// Driver to which the connection belongs. - /// The value for the connection. + /// The value for the connection. public State GetState(NetworkDriver driver) { return driver.GetConnectionState(this); diff --git a/Runtime/NetworkDriver.cs b/Runtime/NetworkDriver.cs index e213b3a..1dcdd82 100644 --- a/Runtime/NetworkDriver.cs +++ b/Runtime/NetworkDriver.cs @@ -14,10 +14,16 @@ namespace Unity.Networking.Transport { + /// + /// Structure used to store a message waiting to be sent on a . + /// public unsafe struct QueuedSendMessage { + /// Content of the message. public fixed byte Data[NetworkParameterConstants.MTU]; + /// Destination endpoint for the message. public NetworkInterfaceEndPoint Dest; + /// Length of the message's content. public int DataLength; } @@ -34,6 +40,7 @@ public struct NetworkDriver : IDisposable /// /// Create a Concurrent Copy of the NetworkDriver. /// + /// Concurrent version of the driver. public Concurrent ToConcurrent() { return new Concurrent @@ -153,7 +160,7 @@ struct PendingSend /// The NetworkConnection id to write through /// A DataStreamWriter to write to /// If you require the payload to be of certain size - /// Returns on a successful acquire. Otherwise returns an indicating the error. + /// Returns on a successful acquire. Otherwise returns an indicating the error. /// Will throw a if the connection is in a Connecting state. public unsafe int BeginSend(NetworkConnection id, out DataStreamWriter writer, int requiredPayloadSize = 0) { @@ -167,7 +174,7 @@ public unsafe int BeginSend(NetworkConnection id, out DataStreamWriter writer, i /// The NetworkConnection id to write through /// A DataStreamWriter to write to /// If you require the payload to be of certain size - /// Returns on a successful acquire. Otherwise returns an indicating the error. + /// Returns on a successful acquire. Otherwise returns an indicating the error. /// Will throw a if the connection is in a Connecting state. public unsafe int BeginSend(NetworkPipeline pipe, NetworkConnection id, out DataStreamWriter writer, int requiredPayloadSize = 0) @@ -219,8 +226,8 @@ public unsafe int BeginSend(NetworkPipeline pipe, NetworkConnection id, totalCapacity -= extraCapacity; } - var result = 0; - if ((result = m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out var sendHandle, m_NetworkSendInterface.UserData, totalCapacity)) != 0) + var sendHandle = default(NetworkInterfaceSendHandle); + if (totalCapacity > NetworkParameterConstants.MTU) { sendHandle.data = (IntPtr)UnsafeUtility.Malloc(totalCapacity, 8, Allocator.Temp); sendHandle.capacity = totalCapacity; @@ -228,6 +235,12 @@ public unsafe int BeginSend(NetworkPipeline pipe, NetworkConnection id, sendHandle.size = 0; sendHandle.flags = SendHandleFlags.AllocatedByDriver; } + else + { + var result = m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData, totalCapacity); + if (result != 0) + return result; + } if (sendHandle.capacity < totalCapacity) return (int)Error.StatusCode.NetworkPacketOverflow; @@ -257,7 +270,7 @@ public unsafe int BeginSend(NetworkPipeline pipe, NetworkConnection id, /// Ends a asynchronous send. /// /// If you require the payload to be of certain size. - /// The length of the buffer sent if nothing went wrong. + /// The length of the buffer sent if nothing went wrong. /// If endsend is called with a matching BeginSend call. /// If the connection got closed between the call of being and end send. public unsafe int EndSend(DataStreamWriter writer) @@ -323,7 +336,7 @@ public unsafe int EndSend(DataStreamWriter writer) /// Aborts a asynchronous send. /// /// If you require the payload to be of certain size. - /// The length of the buffer sent if nothing went wrong. + /// The length of the buffer sent if nothing went wrong. /// If endsend is called with a matching BeginSend call. /// If the connection got closed between the call of being and end send. public unsafe void AbortSend(DataStreamWriter writer) @@ -357,7 +370,7 @@ internal unsafe int CompleteSend(NetworkConnection sendConnection, NetworkInterf { var ret = 0; NetworkInterfaceSendHandle originalHandle = sendHandle; - if ((ret = m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData, originalHandle.size)) != 0) + if ((ret = m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData, NetworkParameterConstants.MTU)) != 0) { return ret; } @@ -532,7 +545,7 @@ public Parameters(NetworkSettings settings) private const int InternalStateBound = 1; /// - /// Gets or sets if the driver is Listening + /// Whether the driver is listening for connections. /// public bool Listening { @@ -540,14 +553,16 @@ public bool Listening private set { m_InternalState[InternalStateListening] = value ? 1 : 0; } } + /// + /// Whether the driver is bound to an endpoint. + /// public bool Bound => m_InternalState[InternalStateBound] == 1; /// /// Helper function for creating a NetworkDriver. /// - /// - /// The for the new NetworkDriver. - /// + /// The for the new driver. + /// The new . /// public static NetworkDriver Create(NetworkSettings settings) { @@ -561,6 +576,7 @@ public static NetworkDriver Create(NetworkSettings settings) /// /// Helper function for creating a NetworkDriver. /// + /// The new . public static NetworkDriver Create() => Create(new NetworkSettings(Allocator.Temp)); /// @@ -568,6 +584,7 @@ public static NetworkDriver Create(NetworkSettings settings) /// /// /// The custom interface to use. + /// The new . public static NetworkDriver Create(N networkInterface) where N : INetworkInterface => Create(networkInterface, new NetworkSettings(Allocator.Temp)); @@ -577,6 +594,7 @@ public static NetworkDriver Create(N networkInterface) where N : INetworkInte /// /// The custom interface to use. /// The for the new NetworkDriver. + /// The new . public static NetworkDriver Create(N networkInterface, NetworkSettings settings) where N : INetworkInterface => new NetworkDriver(networkInterface, settings); @@ -881,7 +899,10 @@ public JobHandle ScheduleUpdate(JobHandle dep = default) /// The job handle public JobHandle ScheduleFlushSend(JobHandle dep) { - return s_NetworkInterfaces[m_NetworkInterfaceIndex].ScheduleSend(m_ParallelSendQueue, dep); + if (Bound) + return s_NetworkInterfaces[m_NetworkInterfaceIndex].ScheduleSend(m_ParallelSendQueue, dep); + else + return dep; } void InternalUpdate() @@ -929,9 +950,8 @@ void InternalUpdate() /// /// Create a new pipeline. /// - /// - /// An array of stages the pipeline should contain. - /// + /// An array of stages the pipeline should contain. + /// The newly-created . /// If the driver is not created properly /// A connection has already been established public NetworkPipeline CreatePipeline(params Type[] stages) @@ -951,7 +971,7 @@ public NetworkPipeline CreatePipeline(params Type[] stages) /// Bind the driver to a endpoint. /// /// The endpoint to bind to. - /// Returns 0 on success. And a negative value if a error occured. + /// Returns 0 on success. And a negative value if a error occured. /// If the driver is not created properly /// If bind is called more then once on the driver /// If bind is called after a connection has already been established @@ -984,7 +1004,7 @@ public int Bind(NetworkEndPoint endpoint) /// /// Set the driver to Listen for incoming connections /// - /// Returns 0 on success. + /// Returns 0 on success. /// If the driver is not created properly /// If listen is called more then once on the driver /// If bind has not been called before calling Listen. @@ -1013,7 +1033,7 @@ public int Listen() /// /// Checks to see if there are any new connections to Accept. /// - /// If accept fails it returns a default NetworkConnection. + /// If accept fails it returns a default NetworkConnection. public NetworkConnection Accept() { if (!Listening) @@ -1033,7 +1053,8 @@ public NetworkConnection Accept() /// /// Connects the driver to a endpoint /// - /// If connect fails it returns a default NetworkConnection. + /// Endpoint to connect to. + /// If connect fails it returns a default NetworkConnection. /// If the driver is not created properly public NetworkConnection Connect(NetworkEndPoint endpoint) { @@ -1093,7 +1114,7 @@ public NetworkConnection Connect(NetworkEndPoint endpoint) /// Disconnects a NetworkConnection /// /// The NetworkConnection we want to Disconnect. - /// Return 0 on success. + /// Return 0 on success. public int Disconnect(NetworkConnection id) { Connection connection; @@ -1194,7 +1215,7 @@ internal int MaxProtocolHeaderSize() /// The NetworkConnection id to write through /// A DataStreamWriter to write to /// If you require the payload to be of certain size - /// Returns on a successful acquire. Otherwise returns an indicating the error. + /// Returns on a successful acquire. Otherwise returns an indicating the error. /// Will throw a if the connection is in a Connecting state. public int BeginSend(NetworkPipeline pipe, NetworkConnection id, out DataStreamWriter writer, int requiredPayloadSize = 0) { @@ -1207,7 +1228,7 @@ public int BeginSend(NetworkPipeline pipe, NetworkConnection id, out DataStreamW /// The NetworkConnection id to write through /// A DataStreamWriter to write to /// If you require the payload to be of certain size - /// Returns on a successful acquire. Otherwise returns an indicating the error. + /// Returns on a successful acquire. Otherwise returns an indicating the error. /// Will throw a if the connection is in a Connecting state. public int BeginSend(NetworkConnection id, out DataStreamWriter writer, int requiredPayloadSize = 0) { @@ -1218,7 +1239,7 @@ public int BeginSend(NetworkConnection id, out DataStreamWriter writer, int requ /// Ends a asynchronous send. /// /// If you require the payload to be of certain size. - /// The length of the buffer sent if nothing went wrong. + /// The length of the buffer sent if nothing went wrong. /// If endsend is called with a matching BeginSend call. /// If the connection got closed between the call of being and end send. public int EndSend(DataStreamWriter writer) @@ -1230,7 +1251,6 @@ public int EndSend(DataStreamWriter writer) /// Aborts a asynchronous send. /// /// If you require the payload to be of certain size. - /// The length of the buffer sent if nothing went wrong. /// If endsend is called with a matching BeginSend call. /// If the connection got closed between the call of being and end send. public void AbortSend(DataStreamWriter writer) @@ -1244,10 +1264,12 @@ public void AbortSend(DataStreamWriter writer) /// Connection on which the event occured. /// Stream reader for the event's data. /// The event's type - /// Returns the type of event received, if the value is a event + /// + /// Returns the type of event received, if the value is a event /// then the DataStreamReader will contain the disconnect reason. If a listening NetworkDriver has received Data /// events from a client, but the NetworkDriver has not Accepted the NetworkConnection yet, the Data event will - /// be discarded. + /// be discarded. + /// public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader reader) { return PopEvent(out con, out reader, out var _); @@ -1306,9 +1328,10 @@ public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReade /// /// Connection for which to pop the next event. /// Stream reader for the event's data. - /// The event's type - /// Returns the type of event received, if the value is a event - /// then the DataStreamReader will contain the disconnect reason. + /// + /// Returns the type of event received, if the value is a event + /// then the DataStreamReader will contain the disconnect reason. + /// public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader reader) { return PopEventForConnection(connectionId, out reader, out var _); @@ -1344,7 +1367,7 @@ public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, o /// Returns the size of the EventQueue for a specific connection /// /// Connection for which to get the event queue size. - /// If the connection is valid it returns the size of the event queue otherwise it returns 0. + /// If the connection is valid it returns the size of the event queue otherwise it returns 0. public int GetEventQueueSizeForConnection(NetworkConnection connectionId) { if (connectionId.m_NetworkId < 0 || connectionId.m_NetworkId >= m_ConnectionList.Length || @@ -1461,6 +1484,7 @@ void CheckTimeouts() { Disconnect(netcon); AddDisconnectEvent(connection.Id, DisconnectReason.Timeout); + connection = m_ConnectionList[i]; } // Check for the heartbeat timeout. diff --git a/Runtime/NetworkEndPoint.cs b/Runtime/NetworkEndPoint.cs index 6be8aa0..f302ed5 100644 --- a/Runtime/NetworkEndPoint.cs +++ b/Runtime/NetworkEndPoint.cs @@ -167,7 +167,7 @@ public ushort RawPort } /// - /// Gets the endpoint's representation as a . + /// Gets the endpoint's representation as a string. /// public string Address => AddressAsString(); @@ -219,7 +219,7 @@ public NetworkEndPoint WithPort(ushort port) public bool IsAny => (this == AnyIpv4.WithPort(Port)) || (this == AnyIpv6.WithPort(Port)); /// - /// Try to parse the given address and port into a new . + /// Try to parse the given address and port into a new . /// /// String representation of the address. /// Port number. @@ -254,7 +254,7 @@ public static bool TryParse(string address, ushort port, out NetworkEndPoint end } /// - /// Same as , except an endpoint is always returned. If the given + /// Same as , except an endpoint is always returned. If the given /// address, port, and family don't represent a valid endpoint, the default one is returned. /// /// String representation of the address. @@ -437,7 +437,7 @@ static Binding.Baselib_NetworkAddress_Family ToBaselibFamily(NetworkFamily famil } /// - /// Representation of an endpoint, to be used internally by . + /// Representation of an endpoint, to be used internally by . /// public unsafe struct NetworkInterfaceEndPoint : IEquatable { @@ -508,6 +508,7 @@ public bool Equals(NetworkInterfaceEndPoint other) /// /// Returns the as a . /// + /// Fixed string representation of the endpoint. public FixedString64Bytes ToFixedString() { if (IsValid == false) @@ -542,7 +543,7 @@ public FixedString64Bytes ToFixedString() } /// - /// Returns the as a . + /// Returns the as a string. /// public override string ToString() { diff --git a/Runtime/NetworkParams.cs b/Runtime/NetworkParams.cs index 9272fdb..0d7b2e5 100644 --- a/Runtime/NetworkParams.cs +++ b/Runtime/NetworkParams.cs @@ -60,6 +60,8 @@ public struct NetworkDataStreamParameter : INetworkParameter /// Size of the default public int size; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() { var valid = true; @@ -100,6 +102,8 @@ public struct NetworkConfigParameter : INetworkParameter /// The main use for this parameter is tests where determinism is more important than correctness. public int fixedFrameTimeMS; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() { var valid = true; @@ -161,6 +165,7 @@ public static ref NetworkSettings WithDataStreamParameters(ref this NetworkSetti /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static NetworkDataStreamParameter GetDataStreamParameters(ref this NetworkSettings settings) { @@ -175,13 +180,14 @@ public static NetworkDataStreamParameter GetDataStreamParameters(ref this Networ /// /// Sets the values for the /// + /// to modify. /// /// /// /// /// /// - /// + /// Modified . public static ref NetworkSettings WithNetworkConfigParameters( ref this NetworkSettings settings, int connectTimeoutMS = NetworkParameterConstants.ConnectTimeoutMS, @@ -210,6 +216,7 @@ public static ref NetworkSettings WithNetworkConfigParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static NetworkConfigParameter GetNetworkConfigParameters(ref this NetworkSettings settings) { diff --git a/Runtime/NetworkPipeline.cs b/Runtime/NetworkPipeline.cs index 69c86b4..8bba650 100644 --- a/Runtime/NetworkPipeline.cs +++ b/Runtime/NetworkPipeline.cs @@ -394,7 +394,9 @@ public static class NetworkPipelineParametersExtensions /// /// Sets the values for the /// + /// to modify. /// + /// Modified . [Obsolete("Will be removed in Unity Transport 2.0.")] public static ref NetworkSettings WithPipelineParameters( ref this NetworkSettings settings, @@ -414,6 +416,7 @@ public static ref NetworkSettings WithPipelineParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static NetworkPipelineParams GetPipelineParameters(ref this NetworkSettings settings) { @@ -438,6 +441,8 @@ public struct NetworkPipelineParams : INetworkParameter /// public int initialCapacity; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() { var valid = true; @@ -533,11 +538,11 @@ public unsafe int Send(NetworkDriver.Concurrent driver, NetworkPipeline pipeline { #if ENABLE_UNITY_COLLECTIONS_CHECKS UnityEngine.Debug.LogError("The parallel network driver needs to process a single unique connection per job, processing a single connection multiple times in a parallel for is not supported."); - return (int)Error.StatusCode.NetworkDriverParallelForErr; -#else - return (int)Error.StatusCode.NetworkDriverParallelForErr; #endif + driver.AbortSend(sendHandle); + return (int)Error.StatusCode.NetworkDriverParallelForErr; } + NativeList currentUpdates = new NativeList(128, Allocator.Temp); int retval = ProcessPipelineSend(driver, 0, pipeline, connection, sendHandle, headerSize, currentUpdates); diff --git a/Runtime/Pipelines/FragmentationPipelineStage.cs b/Runtime/Pipelines/FragmentationPipelineStage.cs index 45cca3e..1127978 100644 --- a/Runtime/Pipelines/FragmentationPipelineStage.cs +++ b/Runtime/Pipelines/FragmentationPipelineStage.cs @@ -15,7 +15,7 @@ namespace Unity.Networking.Transport /// The current implementation of this pipeline stage does not handle reassembly of out-of-order /// fragments. Thus if it is expected that multiple fragmented messages will be in flight at the /// same time, and/or if sending on networks with a lot of jitter, it is recommended to pair - /// this pipeline stage with . + /// this pipeline stage with . ///
[BurstCompile] public unsafe struct FragmentationPipelineStage : INetworkPipelineStage diff --git a/Runtime/Pipelines/FragmentationUtility.cs b/Runtime/Pipelines/FragmentationUtility.cs index d19cf53..cdd61d3 100644 --- a/Runtime/Pipelines/FragmentationUtility.cs +++ b/Runtime/Pipelines/FragmentationUtility.cs @@ -1,11 +1,14 @@ namespace Unity.Networking.Transport.Utilities { + /// Extensions for . public static class FragmentationStageParameterExtensions { /// /// Sets the values for the /// + /// to modify. /// + /// Modified . public static ref NetworkSettings WithFragmentationStageParameters( ref this NetworkSettings settings, int payloadCapacity = FragmentationUtility.Parameters.k_DefaultPayloadCapacity @@ -24,6 +27,7 @@ public static ref NetworkSettings WithFragmentationStageParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static FragmentationUtility.Parameters GetFragmentationStageParameters(ref this NetworkSettings settings) { @@ -36,9 +40,10 @@ public static FragmentationUtility.Parameters GetFragmentationStageParameters(re } } + /// Utility methods and types for the fragmentation pipeline stage. public struct FragmentationUtility { - /// Configuration parameters for . + /// Configuration parameters for . public struct Parameters : INetworkParameter { internal const int k_DefaultPayloadCapacity = 4 * 1024; @@ -46,6 +51,8 @@ public struct Parameters : INetworkParameter /// Maximum payload size that can be fragmented. public int PayloadCapacity; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() { var valid = true; diff --git a/Runtime/Pipelines/ReliableSequencedPipelineStage.cs b/Runtime/Pipelines/ReliableSequencedPipelineStage.cs index 9163a4b..18fa8d0 100644 --- a/Runtime/Pipelines/ReliableSequencedPipelineStage.cs +++ b/Runtime/Pipelines/ReliableSequencedPipelineStage.cs @@ -28,7 +28,7 @@ public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int sta InitializeConnection: InitializeConnectionFunctionPointer, ReceiveCapacity: ReliableUtility.ProcessCapacityNeeded(param), SendCapacity: ReliableUtility.ProcessCapacityNeeded(param), - HeaderCapacity: UnsafeUtility.SizeOf(), + HeaderCapacity: ReliableUtility.PacketHeaderWireSize(param.WindowSize), SharedStateCapacity: ReliableUtility.SharedCapacityNeeded(param) ); } @@ -43,7 +43,7 @@ private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffe requests = NetworkPipelineStage.Requests.SendUpdate; bool needsResume = false; - var header = default(ReliableUtility.PacketHeader); + var header = default(ReliableUtility.ReliableHeader); var slice = default(InboundRecvBuffer); ReliableUtility.Context* reliable = (ReliableUtility.Context*)ctx.internalProcessBuffer; ReliableUtility.SharedContext* shared = (ReliableUtility.SharedContext*)ctx.internalSharedProcessBuffer; @@ -61,7 +61,7 @@ private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffe NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle); #endif var reader = new DataStreamReader(inboundArray); - reader.ReadBytes((byte*)&header, UnsafeUtility.SizeOf()); + reader.ReadBytes((byte*)&header, ReliableUtility.PacketHeaderWireSize(ctx)); if (header.Type == (ushort)ReliableUtility.PacketType.Ack) { @@ -78,7 +78,7 @@ private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffe if (result == nextExpectedSequenceId) { reliable->Delivered = result; - slice = inboundBuffer.Slice(UnsafeUtility.SizeOf()); + slice = inboundBuffer.Slice(ReliableUtility.PacketHeaderWireSize(ctx)); if (needsResume = SequenceHelpers.GreaterThan16((ushort)shared->ReceivedPackets.Sequence, (ushort)result)) { @@ -87,10 +87,14 @@ private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffe } else { - ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf())); + ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(ReliableUtility.PacketHeaderWireSize(ctx))); slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume); } } + else if (result == (int)ReliableUtility.ErrorCodes.Duplicated_Packet) + { + shared->DuplicatesSinceLastAck++; + } } else { @@ -108,7 +112,7 @@ private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer in // Request an update to see if a queued packet needs to be resent later or if an ack packet should be sent requests = NetworkPipelineStage.Requests.Update; - var header = new ReliableUtility.PacketHeader(); + var header = new ReliableUtility.ReliableHeader(); var reliable = (ReliableUtility.Context*)ctx.internalProcessBuffer; // Release any packets that might have been acknowledged since the last call. @@ -127,7 +131,7 @@ private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer in } ctx.header.Clear(); - ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); + ctx.header.WriteBytes((byte*)&header, ReliableUtility.PacketHeaderWireSize(ctx)); reliable->PreviousTimestamp = ctx.timestamp; return (int)Error.StatusCode.Success; } @@ -147,14 +151,14 @@ private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer in requests |= NetworkPipelineStage.Requests.Resume; ctx.header.Clear(); - ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); + ctx.header.WriteBytes((byte*)&header, ReliableUtility.PacketHeaderWireSize(ctx)); reliable->PreviousTimestamp = ctx.timestamp; return (int)Error.StatusCode.Success; } // At this point we know we're in an update call. - // Check if we need to resume (e.g. resent packets). + // Check if we need to resume (e.g. resend packets). reliable->Resume = ReliableUtility.GetNextSendResumeSequence(ctx); if (reliable->Resume != ReliableUtility.NullEntry) requests |= NetworkPipelineStage.Requests.Resume; @@ -164,7 +168,7 @@ private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer in reliable->LastSentTime = ctx.timestamp; ReliableUtility.WriteAckPacket(ctx, ref header); - ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); + ctx.header.WriteBytes((byte*)&header, ReliableUtility.PacketHeaderWireSize(ctx)); reliable->PreviousTimestamp = ctx.timestamp; // TODO: Sending dummy byte over since the pipeline won't send an empty payload (ignored on receive) diff --git a/Runtime/Pipelines/ReliableUtility.cs b/Runtime/Pipelines/ReliableUtility.cs index 20d1cd2..2f7bb1f 100644 --- a/Runtime/Pipelines/ReliableUtility.cs +++ b/Runtime/Pipelines/ReliableUtility.cs @@ -10,16 +10,23 @@ public struct SequenceBufferContext { public int Sequence; public int Acked; + internal ulong AckedMask; + internal ulong LastAckedMask; + + // Unused, only here to maintain the public API intact. public uint AckMask; public uint LastAckMask; } + /// Extensions for . public static class ReliableStageParameterExtensions { /// /// Sets the values for the /// + /// to modify. /// + /// Modified . public static ref NetworkSettings WithReliableStageParameters( ref this NetworkSettings settings, int windowSize = ReliableUtility.ParameterConstants.WindowSize @@ -38,6 +45,7 @@ public static ref NetworkSettings WithReliableStageParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static ReliableUtility.Parameters GetReliableStageParameters(ref this NetworkSettings settings) { @@ -50,33 +58,59 @@ public static ReliableUtility.Parameters GetReliableStageParameters(ref this Net } } + /// Utility methods and types for the reliable pipeline stage. + /// + /// Most methods are meant for internal use only. It is recommended not to rely on anything in + /// in structure since it is very likely to change in a future major version of the package. + /// public struct ReliableUtility { + /// Statistics tracked internally by the reliable pipeline stage. public struct Statistics { + /// Number of packets received by the pipeline stage. public int PacketsReceived; + /// Number of packets sent by the pipeline stage. public int PacketsSent; + /// Number of packets that were dropped in transit. public int PacketsDropped; + /// Number of packets that arrived out of order. public int PacketsOutOfOrder; + /// Number of duplicated packets received by the pipeline stage. public int PacketsDuplicated; + /// Number of stale packets received by the pipeline stage. public int PacketsStale; + /// Number of packets resent by the pipeline stage. public int PacketsResent; } + /// RTT information tracked internally by the reliable pipeline stage. public struct RTTInfo { + /// RTT of the last packet acknowledged. public int LastRtt; + /// Smoothed RTT of the last packets acknowledged. public float SmoothedRtt; + /// Variance of the smoothed RTT. public float SmoothedVariance; + /// Timeout used to resend unacknowledged packets. public int ResendTimeout; } + /// Internal value. Do not use. public const int NullEntry = -1; - // The least amount of time we'll wait until a packet resend is performed - // This is 4x16ms (assumes a 60hz update rate) - public const int DefaultMinimumResendTime = 64; + + /// The least amount of time we'll wait until a packet resend is performed. + public const int DefaultMinimumResendTime = 64; // This is 4x16ms (assumes a 60hz update rate). + + /// The maximum amount of time we'll wait to resend a packet. public const int MaximumResendTime = 200; + // If we receive 3 duplicates AFTER our last send, then it's more likely that one of our + // ACKs was lost and the remote is trying to resend us a packet we won't acknowledge. + internal const int MaxDuplicatesSinceLastAck = 3; + + /// Internal error codes of the pipeline stage. Do not use. public enum ErrorCodes { Stale_Packet = -1, @@ -86,6 +120,7 @@ public enum ErrorCodes InsufficientMemory = -8 } + /// Internal packet types used by the pipeline stage. Do not use. public enum PacketType : ushort { Payload = 0, @@ -103,11 +138,15 @@ public struct SharedContext /// is needed. /// public SequenceBufferContext SentPackets; + /// /// Context of received packets, last sequence ID received, and ackmask of received packets. Acked is not used. /// This is sent back to the remote peer in the header when sending. /// public SequenceBufferContext ReceivedPackets; + + internal int DuplicatesSinceLastAck; + public Statistics stats; public ErrorCodes errorCode; @@ -119,6 +158,7 @@ public struct SharedContext public int RemoteTimerDataStride; } + /// Internal context of the reliable pipeline stage. Do not use. public struct Context { public int Capacity; @@ -132,15 +172,25 @@ public struct Context public long PreviousTimestamp; } + /// Parameters for the reliable pipeline stage. public struct Parameters : INetworkParameter { + /// Maximum number of packets that can be in flight at a time. + /// + /// Must be between 0 and 64. Default is 32. Note that using values higher than 32 will + /// make reliable headers slightly larger, reducing the amount of space available for + /// data. Most of the time the extra bandwidth offered by the larger window size is more + /// than worth it, however. + /// public int WindowSize; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() { var valid = true; - if (WindowSize < 0 || WindowSize > 32) + if (WindowSize < 0 || WindowSize > 64) { valid = false; UnityEngine.Debug.LogError($"{nameof(WindowSize)} value ({WindowSize}) must be greater than 0 and smaller or equal to 32"); @@ -150,12 +200,15 @@ public bool Validate() } } + /// Default values for the reliable pipeline stage parameters. public struct ParameterConstants { + /// Default window size. public const int WindowSize = 32; } [StructLayout(LayoutKind.Sequential)] + [Obsolete("Will be removed in Unity Transport 2.0.")] public struct PacketHeader { public ushort Type; @@ -165,6 +218,18 @@ public struct PacketHeader public uint AckMask; } + [StructLayout(LayoutKind.Sequential)] + internal struct ReliableHeader + { + public ushort Type; + public ushort ProcessingTime; + public ushort SequenceId; + public ushort AckedSequenceId; + // This must be the last member in the packet header, since we truncate it for smaller window sizes. + public ulong AckedMask; + } + + /// Internal packet data structure. Do not use. [StructLayout(LayoutKind.Sequential)] public struct PacketInformation { @@ -174,8 +239,8 @@ public struct PacketInformation public long SendTime; } - // Header is inside the total packet length (Buffer size) [StructLayout(LayoutKind.Explicit)] + [Obsolete("Will be removed in Unity Transport 2.0.")] public unsafe struct Packet { internal const int Length = NetworkParameterConstants.MTU; @@ -183,6 +248,19 @@ public unsafe struct Packet [FieldOffset(0)] public fixed byte Buffer[Length]; } + // Header is inside the total packet length (Buffer size) + [StructLayout(LayoutKind.Explicit)] + internal unsafe struct ReliablePacket + { + // Have to add an extra 4 bytes in there to account for the fact that parts of the + // header will be unused if window size is 32 or less. We could do away with this hack + // by correcting the offsets everywhere else in the code, but that's tricky. + internal const int Length = NetworkParameterConstants.MTU + sizeof(uint); + [FieldOffset(0)] public ReliableHeader Header; + [FieldOffset(0)] public fixed byte Buffer[Length]; + } + + /// Internal packet data structure. Do not use. [StructLayout(LayoutKind.Sequential)] public struct PacketTimers { @@ -198,6 +276,19 @@ private static int AlignedSizeOf() where T : struct return (UnsafeUtility.SizeOf() + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); } + internal static int PacketHeaderWireSize(int windowSize) + { + var fullHeaderSize = UnsafeUtility.SizeOf(); + return windowSize > 32 ? fullHeaderSize : fullHeaderSize - sizeof(uint); + } + + internal static unsafe int PacketHeaderWireSize(NetworkPipelineContext ctx) + { + var reliable = (SharedContext*)ctx.internalSharedProcessBuffer; + var windowSize = reliable->WindowSize; + return PacketHeaderWireSize(windowSize); + } + public static int SharedCapacityNeeded(Parameters param) { // Timers are stored for both remote packets (processing time) and local packets (round trip time) @@ -211,7 +302,7 @@ public static int SharedCapacityNeeded(Parameters param) public static int ProcessCapacityNeeded(Parameters param) { var infoSize = AlignedSizeOf(); - var dataSize = (Packet.Length + UnsafeUtility.SizeOf() + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); + var dataSize = (ReliablePacket.Length + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); infoSize *= param.WindowSize; dataSize *= param.WindowSize; @@ -230,10 +321,10 @@ public static unsafe SharedContext InitializeContext(byte* sharedBuffer, int sha *notifier = new SharedContext { WindowSize = param.WindowSize, - SentPackets = new SequenceBufferContext { Acked = NullEntry }, + SentPackets = new SequenceBufferContext { Acked = NullEntry, AckedMask = ~0ul, LastAckedMask = ~0ul }, MinimumResendTime = DefaultMinimumResendTime, - ReceivedPackets = new SequenceBufferContext { Sequence = NullEntry }, - RttInfo = new RTTInfo { SmoothedVariance = 5, SmoothedRtt = 50, ResendTimeout = 50, LastRtt = 50}, + ReceivedPackets = new SequenceBufferContext { Sequence = NullEntry, AckedMask = ~0ul, LastAckedMask = ~0ul }, + RttInfo = new RTTInfo { SmoothedVariance = 5, SmoothedRtt = 50, ResendTimeout = 50, LastRtt = 50 }, TimerDataOffset = AlignedSizeOf(), TimerDataStride = AlignedSizeOf(), RemoteTimerDataOffset = AlignedSizeOf() + AlignedSizeOf() * param.WindowSize, @@ -254,7 +345,7 @@ public static unsafe int InitializeProcessContext(byte* buffer, int bufferLength ctx->Capacity = param.WindowSize; ctx->IndexStride = AlignedSizeOf(); ctx->IndexPtrOffset = AlignedSizeOf(); - ctx->DataStride = (Packet.Length + UnsafeUtility.SizeOf() + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); + ctx->DataStride = (ReliablePacket.Length + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); ctx->DataPtrOffset = ctx->IndexPtrOffset + (ctx->IndexStride * ctx->Capacity); ctx->Resume = NullEntry; ctx->Delivered = NullEntry; @@ -302,13 +393,28 @@ public static unsafe void SetPacket(byte* self, int sequence, void* data, int le /// The sequence ID of the packet, this is used to find a slot inside the buffer. /// The packet header which we'll store with the packet payload. /// The packet data which we're storing. - /// + [Obsolete("Internal API that shouldn't be used. Will be removed in Unity Transport 2.0.")] public static unsafe void SetHeaderAndPacket(byte* self, int sequence, PacketHeader header, InboundSendBuffer data, long timestamp) + { + throw new NotImplementedException("Implementation was moved to other internal APIs."); + } + + /// + /// Write packet, packet header and tracking information to the given buffer space. This buffer + /// should contain the reliability Context at the front, that contains the capacity of the buffer + /// and pointer offsets needed to find the slots we can copy the packet to. + /// + /// Buffer space where we can store packets. + /// The sequence ID of the packet, this is used to find a slot inside the buffer. + /// The packet header which we'll store with the packet payload. + /// The packet data which we're storing. + /// + internal static unsafe void SetHeaderAndPacket(byte* self, int sequence, ReliableHeader header, InboundSendBuffer data, long timestamp) { Context* ctx = (Context*)self; int totalSize = data.bufferLength + data.headerPadding; - if (totalSize + UnsafeUtility.SizeOf() > ctx->DataStride) + if (totalSize > ctx->DataStride) #if ENABLE_UNITY_COLLECTIONS_CHECKS throw new OverflowException(); #else @@ -322,9 +428,9 @@ public static unsafe void SetHeaderAndPacket(byte* self, int sequence, PacketHea info->HeaderPadding = (ushort)data.headerPadding; info->SendTime = timestamp; - Packet* packet = GetPacket(self, sequence); + ReliablePacket* packet = GetReliablePacket(self, sequence); packet->Header = header; - var offset = (ctx->DataPtrOffset + (index * ctx->DataStride)) + UnsafeUtility.SizeOf(); + var offset = (ctx->DataPtrOffset + (index * ctx->DataStride)); void* dataPtr = (self + offset); if (data.bufferLength > 0) @@ -339,13 +445,19 @@ public static unsafe void SetHeaderAndPacket(byte* self, int sequence, PacketHea return (PacketInformation*)((self + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); } + [Obsolete("Internal API that shouldn't be used. Will be removed in Unity Transport 2.0.")] public static unsafe Packet* GetPacket(byte* self, int sequence) + { + throw new NotImplementedException("Implementation was moved to other internal APIs."); + } + + internal static unsafe ReliablePacket* GetReliablePacket(byte* self, int sequence) { Context* ctx = (Context*)self; var index = sequence % ctx->Capacity; var offset = ctx->DataPtrOffset + (index * ctx->DataStride); - return (Packet*)(self + offset); + return (ReliablePacket*)(self + offset); } public static unsafe bool TryAquire(byte* self, int sequence) @@ -436,7 +548,7 @@ internal static unsafe void ReleaseAcknowledgedPackets(NetworkPipelineContext co SharedContext* reliable = (SharedContext*)context.internalSharedProcessBuffer; // Last sequence ID and ackmask we received from the remote peer. - var lastReceivedAckMask = reliable->SentPackets.AckMask; + var lastReceivedAckedMask = reliable->SentPackets.AckedMask; var lastOwnSequenceIdAckedByRemote = (ushort)reliable->SentPackets.Acked; var sequence = GetNonWrappingLastAckedSequenceNumber(context); @@ -451,12 +563,12 @@ internal static unsafe void ReleaseAcknowledgedPackets(NetworkPipelineContext co // Check the bit for this sequence ID against the ackmask. Bit 0 in the ackmask // is the latest acked sequence ID, bit 1 latest minus 1 (one older) and so on. // If bit X is 1 then last acked sequence ID minus X is acknowledged. - var ackBits = 1 << (lastOwnSequenceIdAckedByRemote - info->SequenceId); + var ackBits = 1ul << (lastOwnSequenceIdAckedByRemote - info->SequenceId); // Release if this ID has been flipped on in the ackmask (it's acknowledged). // Ignore if sequence ID is out of window range of the last acknowledged ID. var distance = SequenceHelpers.AbsDistance(lastOwnSequenceIdAckedByRemote, (ushort)info->SequenceId); - if (distance < reliable->WindowSize && (ackBits & lastReceivedAckMask) != 0) + if (distance < reliable->WindowSize && (ackBits & lastReceivedAckedMask) != 0) { Release(context.internalProcessBuffer, info->SequenceId); info->SendTime = -1; @@ -522,7 +634,7 @@ public static unsafe InboundRecvBuffer ResumeReceive(NetworkPipelineContext cont inBuffer.bufferLength = info->Size; reliable->Delivered = startSequence; - if ((ushort)(startSequence + 1) <= latestReceivedPacket) + if (SequenceHelpers.LessThan16((ushort)startSequence, (ushort)latestReceivedPacket)) { reliable->Resume = (ushort)(startSequence + 1); needsResume = true; @@ -541,8 +653,23 @@ public static unsafe InboundRecvBuffer ResumeReceive(NetworkPipelineContext cont /// Packet header for the packet payload we're resending. /// Indicates if a pipeline resume is needed again. Unused. /// Buffer slice to packet payload. - /// + [Obsolete("Will be removed in Unity Transport 2.0.")] public static unsafe InboundSendBuffer ResumeSend(NetworkPipelineContext context, out PacketHeader header, ref bool needsResume) + { + throw new NotImplementedException("Implementation moved to an internal method. Shouldn't be used anymore."); + } + + /// + /// Resend a packet which we have not received an acknowledgement for in time. Pipeline resume + /// will be enabled if there are more packets which we need to resend. The send reliability context + /// will then also be updated to track the next packet we need to resume. + /// + /// Pipeline context, we'll use both the shared reliability context and send context. + /// Packet header for the packet payload we're resending. + /// Indicates if a pipeline resume is needed again. Unused. + /// Buffer slice to packet payload. + /// + internal static unsafe InboundSendBuffer ResumeSend(NetworkPipelineContext context, out ReliableHeader header, ref bool needsResume) { SharedContext* reliable = (SharedContext*)context.internalSharedProcessBuffer; Context* ctx = (Context*)context.internalProcessBuffer; @@ -559,14 +686,14 @@ public static unsafe InboundSendBuffer ResumeSend(NetworkPipelineContext context // Reset the resend timer information->SendTime = context.timestamp; - Packet *packet = GetPacket(context.internalProcessBuffer, sequence); + ReliablePacket *packet = GetReliablePacket(context.internalProcessBuffer, sequence); header = packet->Header; // Update acked/ackmask to latest values header.AckedSequenceId = (ushort)reliable->ReceivedPackets.Sequence; - header.AckMask = reliable->ReceivedPackets.AckMask; + header.AckedMask = reliable->ReceivedPackets.AckedMask; - var offset = (ctx->DataPtrOffset + ((sequence % ctx->Capacity) * ctx->DataStride)) + UnsafeUtility.SizeOf(); + var offset = (ctx->DataPtrOffset + ((sequence % ctx->Capacity) * ctx->DataStride)); var inbound = default(InboundSendBuffer); inbound.bufferWithHeaders = context.internalProcessBuffer + offset; @@ -586,7 +713,21 @@ public static unsafe InboundSendBuffer ResumeSend(NetworkPipelineContext context /// Buffer with packet data. /// Packet header which will be populated. /// Sequence ID assigned to this packet. + [Obsolete("Will be removed in Unity Transport 2.0.")] public static unsafe int Write(NetworkPipelineContext context, InboundSendBuffer inboundBuffer, ref PacketHeader header) + { + throw new NotImplementedException("Implementation moved to an internal method. Shouldn't be used anymore."); + } + + /// + /// Store the packet for possible later resends, and fill in the header we'll use to send it (populate with + /// sequence ID, last acknowledged ID from remote with ackmask. + /// + /// Pipeline context, the reliability shared state is used here. + /// Buffer with packet data. + /// Packet header which will be populated. + /// Sequence ID assigned to this packet. + internal static unsafe int Write(NetworkPipelineContext context, InboundSendBuffer inboundBuffer, ref ReliableHeader header) { SharedContext* reliable = (SharedContext*)context.internalSharedProcessBuffer; @@ -601,10 +742,11 @@ public static unsafe int Write(NetworkPipelineContext context, InboundSendBuffer header.SequenceId = sequence; header.AckedSequenceId = (ushort)reliable->ReceivedPackets.Sequence; - header.AckMask = reliable->ReceivedPackets.AckMask; + header.AckedMask = reliable->ReceivedPackets.AckedMask; reliable->ReceivedPackets.Acked = reliable->ReceivedPackets.Sequence; - reliable->ReceivedPackets.LastAckMask = header.AckMask; + reliable->ReceivedPackets.LastAckedMask = header.AckedMask; + reliable->DuplicatesSinceLastAck = 0; // Attach our processing time of the packet we're acknowledging (time between receiving it and sending this ack) header.ProcessingTime = @@ -625,17 +767,31 @@ public static unsafe int Write(NetworkPipelineContext context, InboundSendBuffer /// Pipeline context, the reliability shared state is used here. /// Packet header which will be populated. /// + [Obsolete("Will be removed in Unity Transport 2.0.")] public static unsafe void WriteAckPacket(NetworkPipelineContext context, ref PacketHeader header) + { + throw new NotImplementedException("Implementation moved to an internal method. Shouldn't be used anymore."); + } + + /// + /// Write an ack packet, only the packet header is used and this doesn't advance the sequence ID. + /// The packet is not stored away for resend routine. + /// + /// Pipeline context, the reliability shared state is used here. + /// Packet header which will be populated. + /// + internal static unsafe void WriteAckPacket(NetworkPipelineContext context, ref ReliableHeader header) { SharedContext* reliable = (SharedContext*)context.internalSharedProcessBuffer; header.Type = (ushort)PacketType.Ack; header.AckedSequenceId = (ushort)reliable->ReceivedPackets.Sequence; - header.AckMask = reliable->ReceivedPackets.AckMask; + header.AckedMask = reliable->ReceivedPackets.AckedMask; header.ProcessingTime = CalculateProcessingTime(context.internalSharedProcessBuffer, header.AckedSequenceId, context.timestamp); reliable->ReceivedPackets.Acked = reliable->ReceivedPackets.Sequence; - reliable->ReceivedPackets.LastAckMask = header.AckMask; + reliable->ReceivedPackets.LastAckedMask = header.AckedMask; + reliable->DuplicatesSinceLastAck = 0; } public static unsafe void StoreTimestamp(byte* sharedBuffer, ushort sequenceId, long timestamp) @@ -717,7 +873,21 @@ public static unsafe ushort CalculateProcessingTime(byte* sharedBuffer, ushort s /// Pipeline context, the reliability shared state is used here. /// Packet header of a new received packet. /// Sequence ID of the received packet. + [Obsolete("Will be removed in Unity Transport 2.0.")] public static unsafe int Read(NetworkPipelineContext context, PacketHeader header) + { + throw new NotImplementedException("Implementation moved to an internal method. Shouldn't be used anymore."); + } + + /// + /// Read header data and update reliability tracking information in the shared context. + /// - If the packets sequence ID is lower than the last received ID+1, then it's stale + /// - If the packets sequence ID is higher, then we'll process it and update tracking info in the shared context + /// + /// Pipeline context, the reliability shared state is used here. + /// Packet header of a new received packet. + /// Sequence ID of the received packet. + internal static unsafe int Read(NetworkPipelineContext context, ReliableHeader header) { SharedContext* reliable = (SharedContext*)context.internalSharedProcessBuffer; @@ -732,23 +902,23 @@ public static unsafe int Read(NetworkPipelineContext context, PacketHeader heade } var window = reliable->WindowSize - 1; - if (SequenceHelpers.GreaterThan16((ushort)(header.SequenceId + 1), (ushort)reliable->ReceivedPackets.Sequence)) + if (SequenceHelpers.GreaterThan16(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence)) { int distance = SequenceHelpers.AbsDistance(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence); if (distance > window) { reliable->stats.PacketsDropped += distance - 1; - reliable->ReceivedPackets.AckMask = 1; + reliable->ReceivedPackets.AckedMask = 1; } else { - reliable->ReceivedPackets.AckMask <<= distance; - reliable->ReceivedPackets.AckMask |= 1; + reliable->ReceivedPackets.AckedMask <<= distance; + reliable->ReceivedPackets.AckedMask |= 1; for (var i = 0; i < Math.Min(distance, window); ++i) { - if ((reliable->ReceivedPackets.AckMask & 1 << i) == 0) + if ((reliable->ReceivedPackets.AckedMask & 1ul << i) == 0) { reliable->stats.PacketsDropped++; } @@ -757,15 +927,15 @@ public static unsafe int Read(NetworkPipelineContext context, PacketHeader heade reliable->ReceivedPackets.Sequence = header.SequenceId; } - else if (SequenceHelpers.LessThan16(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence)) + else { int distance = SequenceHelpers.AbsDistance(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence); // If this is a resent packet the distance will seem very big and needs to be calculated again with adjustment for wrapping if (distance >= ushort.MaxValue - reliable->WindowSize) distance = reliable->ReceivedPackets.Sequence - header.SequenceId; - var ackBit = 1 << distance; - if ((ackBit & reliable->ReceivedPackets.AckMask) != 0) + var ackBit = 1ul << distance; + if ((ackBit & reliable->ReceivedPackets.AckedMask) != 0) { // Still valuable to check ACKs in a duplicated packet, since there might be // more information than on the original packet if it's a resend. @@ -776,7 +946,7 @@ public static unsafe int Read(NetworkPipelineContext context, PacketHeader heade } reliable->stats.PacketsOutOfOrder++; - reliable->ReceivedPackets.AckMask |= (uint)ackBit; + reliable->ReceivedPackets.AckedMask |= (ulong)ackBit; } // Store receive timestamp for remote sequence ID we just received @@ -787,7 +957,13 @@ public static unsafe int Read(NetworkPipelineContext context, PacketHeader heade return header.SequenceId; } + [Obsolete("Will be removed in Unity Transport 2.0.")] public static unsafe void ReadAckPacket(NetworkPipelineContext context, PacketHeader header) + { + throw new NotImplementedException("Implementation moved to an internal method. Shouldn't be used anymore."); + } + + internal static unsafe void ReadAckPacket(NetworkPipelineContext context, ReliableHeader header) { SharedContext* reliable = (SharedContext*)context.internalSharedProcessBuffer; @@ -805,12 +981,12 @@ public static unsafe void ReadAckPacket(NetworkPipelineContext context, PacketHe if (reliable->SentPackets.Acked == header.AckedSequenceId) { // If the current packet is the same as the last one we acked we do not know which one is newer, but it is safe to keep any packet acked by either ack since we never un-ack - reliable->SentPackets.AckMask |= header.AckMask; + reliable->SentPackets.AckedMask |= header.AckedMask; } else { reliable->SentPackets.Acked = header.AckedSequenceId; - reliable->SentPackets.AckMask = header.AckMask; + reliable->SentPackets.AckedMask = header.AckedMask; } } @@ -821,10 +997,12 @@ public static unsafe bool ShouldSendAck(NetworkPipelineContext ctx) // If more than one full frame (timestamp - prevTimestamp = one frame) has elapsed then send ack packet // and if the last received sequence ID has not been acked yet, or the set of acked packet in the window - // changed without the sequence ID updating (can happen when receiving out of order packets) + // changed without the sequence ID updating (can happen when receiving out of order packets), or we've + // received a lot of duplicates since last sending a ACK. if (reliable->LastSentTime < reliable->PreviousTimestamp && (SequenceHelpers.LessThan16((ushort)shared->ReceivedPackets.Acked, (ushort)shared->ReceivedPackets.Sequence) || - shared->ReceivedPackets.AckMask != shared->ReceivedPackets.LastAckMask)) + shared->ReceivedPackets.AckedMask != shared->ReceivedPackets.LastAckedMask || + shared->DuplicatesSinceLastAck >= MaxDuplicatesSinceLastAck)) return true; return false; } diff --git a/Runtime/Pipelines/SimulatorUtility.cs b/Runtime/Pipelines/SimulatorUtility.cs index 0091cc6..3ce6100 100644 --- a/Runtime/Pipelines/SimulatorUtility.cs +++ b/Runtime/Pipelines/SimulatorUtility.cs @@ -4,11 +4,13 @@ namespace Unity.Networking.Transport.Utilities { + /// Extensions for . public static class SimulatorStageParameterExtensions { /// /// Sets the values for the /// + /// to modify. /// /// /// @@ -18,6 +20,7 @@ public static class SimulatorStageParameterExtensions /// /// /// + /// Modified . public static ref NetworkSettings WithSimulatorStageParameters( ref this NetworkSettings settings, int maxPacketCount, @@ -52,6 +55,7 @@ public static ref NetworkSettings WithSimulatorStageParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static SimulatorUtility.Parameters GetSimulatorStageParameters(ref this NetworkSettings settings) { @@ -62,6 +66,11 @@ public static SimulatorUtility.Parameters GetSimulatorStageParameters(ref this N } } + /// Utility methods and types for the simulator pipeline stage. + /// + /// Most methods are meant for internal use only. It is recommended not to rely on anything in + /// in structure since it is very likely to change in a future major version of the package. + /// public struct SimulatorUtility { private int m_PacketCount; @@ -124,9 +133,12 @@ public struct Parameters : INetworkParameter /// public uint RandomSeed; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() => true; } + /// Internal context of the simulator pipeline. Do not use. [StructLayout(LayoutKind.Sequential)] public struct Context { diff --git a/Runtime/Relay/RelayNetworkProtocol.cs b/Runtime/Relay/RelayNetworkProtocol.cs index dd8298a..1ded40e 100644 --- a/Runtime/Relay/RelayNetworkProtocol.cs +++ b/Runtime/Relay/RelayNetworkProtocol.cs @@ -30,13 +30,16 @@ public static unsafe ref RelayAllocationId AsRelayAllocationId(this ref NetworkI } } + /// Extensions for . public static class RelayParameterExtensions { /// /// Sets the values for the /// + /// to modify. /// /// + /// Modified . public static ref NetworkSettings WithRelayParameters( ref this NetworkSettings settings, ref RelayServerData serverData, @@ -57,6 +60,7 @@ public static ref NetworkSettings WithRelayParameters( /// /// Gets the /// + /// to get parameters from. /// Returns the values for the public static RelayNetworkParameter GetRelayParameters(ref this NetworkSettings settings) { @@ -87,6 +91,8 @@ public struct RelayNetworkParameter : INetworkParameter /// public int RelayConnectionTimeMS; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public unsafe bool Validate() { var valid = true; diff --git a/Runtime/SecureProtocol/SecureParameters.cs b/Runtime/SecureProtocol/SecureParameters.cs index 52dac2f..b7ed2d4 100644 --- a/Runtime/SecureProtocol/SecureParameters.cs +++ b/Runtime/SecureProtocol/SecureParameters.cs @@ -50,16 +50,21 @@ public struct SecureNetworkProtocolParameter : INetworkParameter /// Minimum secure handshake timeout (milliseconds, defaults to 1000). public uint SSLHandshakeTimeoutMin; + /// Validate the settings. + /// True if the settings are valid, false otherwise. public bool Validate() => true; } + /// Extensions for . public static class SecureParameterExtensions { /// Set client security parameters (for use with official CA certificates). + /// to modify. /// Hostname of the server to connect to. /// Secure read timeout (in milliseconds). /// Maximum handshake timeout (in milliseconds). /// Minimum handshake timeout (in milliseconds). + /// Modified . public static ref NetworkSettings WithSecureClientParameters( ref this NetworkSettings settings, ref FixedString32Bytes serverName, @@ -86,7 +91,9 @@ public static ref NetworkSettings WithSecureClientParameters( } /// Set client security parameters (for use with official CA certificates). + /// to modify. /// Hostname of the server to connect to. + /// Modified . public static ref NetworkSettings WithSecureClientParameters( ref this NetworkSettings settings, string serverName) @@ -99,11 +106,13 @@ public static ref NetworkSettings WithSecureClientParameters( } /// Set client security parameters (server authentication only). + /// to modify. /// CA certificate that signed the server's certificate (PEM format). /// Common name (CN) in the server certificate. /// Secure read timeout (in milliseconds). /// Maximum handshake timeout (in milliseconds). /// Minimum handshake timeout (in milliseconds). + /// Modified . public static ref NetworkSettings WithSecureClientParameters( ref this NetworkSettings settings, ref FixedString4096Bytes caCertificate, @@ -132,8 +141,10 @@ public static ref NetworkSettings WithSecureClientParameters( } /// Set client security parameters (server authentication only). + /// to modify. /// CA certificate that signed the server's certificate (PEM format). /// Common name (CN) in the server certificate. + /// Modified . public static ref NetworkSettings WithSecureClientParameters( ref this NetworkSettings settings, string caCertificate, @@ -148,6 +159,7 @@ public static ref NetworkSettings WithSecureClientParameters( } /// Set client security parameters (for client authentication). + /// to modify. /// Client's certificate (PEM format). /// Client's private key (PEM format). /// CA certificate that signed the server's certificate (PEM format). @@ -155,6 +167,7 @@ public static ref NetworkSettings WithSecureClientParameters( /// Secure read timeout (in milliseconds). /// Maximum handshake timeout (in milliseconds). /// Minimum handshake timeout (in milliseconds). + /// Modified . public static ref NetworkSettings WithSecureClientParameters( ref this NetworkSettings settings, ref FixedString4096Bytes certificate, @@ -185,10 +198,12 @@ public static ref NetworkSettings WithSecureClientParameters( } /// Set client security parameters (for client authentication). + /// to modify. /// Client's certificate (PEM format). /// Client's private key (PEM format). /// CA certificate that signed the server's certificate (PEM format). /// Common name (CN) in the server certificate. + /// Modified . public static ref NetworkSettings WithSecureClientParameters( ref this NetworkSettings settings, string certificate, @@ -211,11 +226,13 @@ public static ref NetworkSettings WithSecureClientParameters( } /// Set server security parameters (server authentication only). + /// to modify. /// Server's certificate chain (PEM format). /// Server's private key (PEM format). /// Secure read timeout (in milliseconds). /// Maximum handshake timeout (in milliseconds). /// Minimum handshake timeout (in milliseconds). + /// Modified . public static ref NetworkSettings WithSecureServerParameters( ref this NetworkSettings settings, ref FixedString4096Bytes certificate, @@ -244,8 +261,10 @@ public static ref NetworkSettings WithSecureServerParameters( } /// Set server security parameters (server authentication only). + /// to modify. /// Server's certificate chain (PEM format). /// Server's private key (PEM format). + /// Modified . public static ref NetworkSettings WithSecureServerParameters( ref this NetworkSettings settings, string certificate, @@ -260,6 +279,7 @@ public static ref NetworkSettings WithSecureServerParameters( } /// Set server security parameters (for client authentication). + /// to modify. /// Server's certificate chain (PEM format). /// Server's private key (PEM format). /// CA certificate that signed the client certificates (PEM format). @@ -268,6 +288,7 @@ public static ref NetworkSettings WithSecureServerParameters( /// Secure read timeout (in milliseconds). /// Maximum handshake timeout (in milliseconds). /// Minimum handshake timeout (in milliseconds). + /// Modified . public static ref NetworkSettings WithSecureServerParameters( ref this NetworkSettings settings, ref FixedString4096Bytes certificate, @@ -299,11 +320,13 @@ public static ref NetworkSettings WithSecureServerParameters( } /// Set server security parameters (for client authentication). + /// to modify. /// Server's certificate chain (PEM format). /// Server's private key (PEM format). /// CA certificate that signed the client certificates (PEM format). /// Common name (CN) in the client certificates. /// Client authentication policy. + /// Modified . public static ref NetworkSettings WithSecureServerParameters( ref this NetworkSettings settings, string certificate, @@ -390,6 +413,11 @@ public static ref NetworkSettings WithSecureParameters( return ref settings; } + /// + /// Get the from the settings. + /// + /// to get parameters from. + /// The stored in the settings. public static SecureNetworkProtocolParameter GetSecureParameters(ref this NetworkSettings settings) { if (!settings.TryGet(out var parameters)) diff --git a/Runtime/Utilities.cs b/Runtime/Utilities.cs index 82ef48f..6d25c46 100644 --- a/Runtime/Utilities.cs +++ b/Runtime/Utilities.cs @@ -152,7 +152,6 @@ public void Clear(int bucket) /// /// Utility class used when dealing with sequenced pipeline stages. /// - /// public static class SequenceHelpers { /// @@ -260,7 +259,7 @@ public static class FixedStringHexExt /// /// Appends the hex using the specified str /// - /// The string type. Has constraints where it must be a struct, an INativeList and IUTF8Bytes. + /// The string type. Has constraints where it must be unmanaged, an INativeList<byte> and IUTF8Bytes. /// The string of type T. Passed in by reference, and will be modified by this method. /// The ushort representation of the hex value to convert to its string representation and append to T. /// The from the attempt to modify , either None or Overflow. @@ -289,7 +288,7 @@ public static FormatError AppendHex(ref this T str, ushort val) where T : unm } /// - /// Provides Extension methods for the class + /// Provides Extension methods for the class /// public static class NativeListExt { @@ -326,6 +325,7 @@ public static void ResizeUninitializedTillPowerOf2(this NativeList list, i /// public static class RandomHelpers { + /// Get a random ushort value. /// a ushort in [1..ushort.MaxValue - 1] range public static ushort GetRandomUShort() { @@ -333,6 +333,7 @@ public static ushort GetRandomUShort() return (ushort)rnd.NextUInt(1, ushort.MaxValue - 1); } + /// Get a random ulong value. /// a ushort in [1..uint.MaxValue - 1] range public static ulong GetRandomULong() { diff --git a/Samples~/Soaker/Scripts/SoakCommon.cs b/Samples~/Soaker/Scripts/SoakCommon.cs index 48cc127..33e2189 100644 --- a/Samples~/Soaker/Scripts/SoakCommon.cs +++ b/Samples~/Soaker/Scripts/SoakCommon.cs @@ -121,10 +121,8 @@ public static unsafe void DumpReliabilityStatistics(NetworkDriver driver, Networ "PacketsSent: " + relCtx->stats.PacketsSent + "\n" + "PacketsStale: " + relCtx->stats.PacketsStale + "\n" + "Last received remote seqId: " + relCtx->ReceivedPackets.Sequence + "\n" + - "Last received remote ackMask: " + SequenceHelpers.BitMaskToString(relCtx->ReceivedPackets.AckMask) + "\n" + "Last sent seqId: " + (relCtx->SentPackets.Sequence - 1)+ "\n" + - "Last acked seqId: " + relCtx->SentPackets.Acked + "\n" + - "Last ackmask: " + SequenceHelpers.BitMaskToString(relCtx->SentPackets.AckMask));*/ + "Last acked seqId: " + relCtx->SentPackets.Acked);*/ } public static unsafe void GatherReliabilityStats(ref SoakStatisticsPoint stats, ref SoakStatisticsPoint lastStats, NetworkDriver driver, diff --git a/Tests/Editor/ReliablePipelineTests.cs b/Tests/Editor/ReliablePipelineTests.cs index 612e8e0..7f079b2 100644 --- a/Tests/Editor/ReliablePipelineTests.cs +++ b/Tests/Editor/ReliablePipelineTests.cs @@ -127,7 +127,7 @@ public unsafe void ReliableUtility_ValidationScenarios() ReliableUtility.Parameters parameters = new ReliableUtility.Parameters { - WindowSize = 32 + WindowSize = 64 }; var processCapacity = ReliableUtility.ProcessCapacityNeeded(parameters); @@ -145,10 +145,10 @@ public unsafe void ReliableUtility_ValidationScenarios() var ep2RecvBuffer = new NativeArray(processCapacity, Allocator.Persistent); // packet - var packet = new NativeArray(UnsafeUtility.SizeOf(), Allocator.Persistent); + var packet = new NativeArray(UnsafeUtility.SizeOf(), Allocator.Persistent); packet[0] = 100; - var header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); + var header = new DataStreamWriter(ReliableUtility.PacketHeaderWireSize(parameters.WindowSize), Allocator.Temp); ReliableSequencedPipelineStage ep1Owner = new ReliableSequencedPipelineStage(); ReliableSequencedPipelineStage ep2Owner = new ReliableSequencedPipelineStage(); @@ -260,7 +260,7 @@ public unsafe void ReliableUtility_ValidationScenarios() var info = ReliableUtility.GetPacketInformation((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); var offset = ep1sendContext->DataPtrOffset + ((seq % ep1sendContext->Capacity) * ep1sendContext->DataStride); - var inspectPacket = ReliableUtility.GetPacket((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); + var inspectPacket = ReliableUtility.GetReliablePacket((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); InboundRecvBuffer data; data.buffer = (byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr() + offset; @@ -299,7 +299,7 @@ public unsafe void ReliableUtility_ValidationScenarios() var info = ReliableUtility.GetPacketInformation((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); var offset = ep1sendContext->DataPtrOffset + ((seq % ep1sendContext->Capacity) * ep1sendContext->DataStride); - var inspectPacket = ReliableUtility.GetPacket((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); + var inspectPacket = ReliableUtility.GetReliablePacket((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); data.buffer = (byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr() + offset; data.bufferLength = info->Size; @@ -374,13 +374,13 @@ public unsafe void ReliableUtility_Validation() ReliableUtility.SetPacket(processBufferPtr, 0, (byte*)buffer.GetUnsafeReadOnlyPtr(), buffer.Length); - var slice = ReliableUtility.GetPacket(processBufferPtr, 0); + var slice = ReliableUtility.GetReliablePacket(processBufferPtr, 0); Assert.IsTrue(slice->Buffer[0] == buffer[0]); for (int i = 0; i < capacity * 5; i++) { ReliableUtility.SetPacket(processBufferPtr, i, (byte*)buffer.GetUnsafeReadOnlyPtr(), buffer.Length); - slice = ReliableUtility.GetPacket(processBufferPtr, i); + slice = ReliableUtility.GetReliablePacket(processBufferPtr, i); Assert.IsTrue(slice->Buffer[0] == buffer[0]); } ReliableUtility.Release(processBufferPtr, 0, 5); @@ -418,9 +418,9 @@ public unsafe void ReliableUtility_AckPackets_SeqIdBeginAt0() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 0; // Last sent is initialized to what you are sending next sharedContext->SentPackets.Acked = -1; - sharedContext->SentPackets.AckMask = 0x1; + sharedContext->SentPackets.AckedMask = 0x1; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -479,9 +479,9 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap1() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; sharedContext->SentPackets.Acked = 2; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -541,9 +541,9 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap2() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 0; sharedContext->SentPackets.Acked = 65535; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -596,9 +596,9 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap3() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 17; sharedContext->SentPackets.Acked = 16; - sharedContext->SentPackets.AckMask = 0xFFFFDBB7; + sharedContext->SentPackets.AckedMask = 0xFFFFFFFFDBB7; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -648,9 +648,9 @@ public unsafe void ReliableUtility_AckPackets_ReleaseSlotWithWrappedSeqId() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 1; sharedContext->SentPackets.Acked = 0; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -676,7 +676,7 @@ public unsafe void ReliableUtility_AckPackets_ReleaseSlotWithWrappedSeqId() } [Test] - public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly1() + public unsafe void ReliableUtility_AckPackets_AckedMaskShiftsProperly1() { ReliableUtility.Parameters parameters = new ReliableUtility.Parameters { @@ -704,9 +704,9 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly1() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 4; sharedContext->SentPackets.Acked = 3; - sharedContext->SentPackets.AckMask = 0xFFFFFFFD; // bit 0 = seqId 3 (1), bit 1 = seqId 2 (0) + sharedContext->SentPackets.AckedMask = 0xFFFFFFFFFFFD; // bit 0 = seqId 3 (1), bit 1 = seqId 2 (0) sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -739,7 +739,7 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly1() } [Test] - public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly2() + public unsafe void ReliableUtility_AckPackets_AckedMaskShiftsProperly2() { ReliableUtility.Parameters parameters = new ReliableUtility.Parameters { @@ -767,9 +767,9 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly2() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 5; sharedContext->SentPackets.Acked = 4; - sharedContext->SentPackets.AckMask = 0xFFFFFFFD; // bit 0 = seqId 4 (1), bit 1 = seqId 3 (0) + sharedContext->SentPackets.AckedMask = 0xFFFFFFFFFFFD; // bit 0 = seqId 4 (1), bit 1 = seqId 3 (0) sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -894,9 +894,9 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint1() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; // what was last sent doesn't matter here sharedContext->SentPackets.Acked = 2; - sharedContext->SentPackets.AckMask = 0xFFFFFFF7; // bit 0,1,2 maps to seqId 2,1,0 all delivered, bit 3 is seqId 65535 which is not yet delivered + sharedContext->SentPackets.AckedMask = 0xFFFFFFFFFFF7; // bit 0,1,2 maps to seqId 2,1,0 all delivered, bit 3 is seqId 65535 which is not yet delivered sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 65534; // latest in sequence delivered packet, one less than what unclogs the packet jam @@ -922,7 +922,7 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint1() // Generate the packet which will be handled in receive InboundRecvBuffer packet = default; - GeneratePacket(9000, 2, 0xFFFFFFFF, 65535, ref sendBuffer, out packet); + GeneratePacket(9000, 2, ~0ul, 65535, ref sendBuffer, out packet); // Process 65535, 0 should then be next in line on the resume field var stageRequest = NetworkPipelineStage.Requests.None; @@ -982,9 +982,9 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint2() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 2; // what was last sent doesn't matter here sharedContext->SentPackets.Acked = 1; - sharedContext->SentPackets.AckMask = 0xFFFFFFF7; // bit 0,1,2 maps to seqId 1,0,65535 all delivered, bit 3 is seqId 65534 which is not yet delivered + sharedContext->SentPackets.AckedMask = 0xFFFFFFFFFFF7; // bit 0,1,2 maps to seqId 1,0,65535 all delivered, bit 3 is seqId 65534 which is not yet delivered sharedContext->ReceivedPackets.Sequence = 1; - sharedContext->ReceivedPackets.AckMask = 0xFFFFFFF7; + sharedContext->ReceivedPackets.AckedMask = 0xFFFFFFFFFFF7; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 65533; // latest in sequence delivered packet, one less than what unclogs the packet jam @@ -1009,7 +1009,7 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint2() // Generate the packet which will be handled in receive InboundRecvBuffer packet = default; - GeneratePacket(9000, 65533, 0xFFFFFFFF, 65534, ref sendBuffer, out packet); + GeneratePacket(9000, 65533, ~0ul, 65534, ref sendBuffer, out packet); // Process 65534, 65535 should then be next in line on the resume field var stageRequest = NetworkPipelineStage.Requests.None; @@ -1042,7 +1042,7 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint2() } [Test] - public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProperly() + public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckedMaskProperly() { ReliableUtility.Parameters parameters = new ReliableUtility.Parameters { @@ -1069,9 +1069,9 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProper var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 99; // what was last sent doesn't matter here sharedContext->SentPackets.Acked = 97; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = 98; - sharedContext->ReceivedPackets.AckMask = 0xFFFFFFF7; + sharedContext->ReceivedPackets.AckedMask = 0xFFFFFFFFFFFFFFF7; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 94; // latest in sequence delivered packet, one less than what unclogs the packet jam @@ -1095,7 +1095,7 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProper ReliableUtility.SetPacket(recvBufferPtr, 98, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); InboundRecvBuffer packet = default; - GeneratePacket(9000, 98, 0xFFFFFFFF, 99, ref sendBuffer, out packet); + GeneratePacket(9000, 98, ~0ul, 99, ref sendBuffer, out packet); // Receive 99, it's out of order so should be queued for later (waiting for 95) var stageRequest = NetworkPipelineStage.Requests.None; @@ -1104,7 +1104,7 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProper Assert.AreEqual(-1, receiveContext->Resume); Assert.AreEqual(NetworkPipelineStage.Requests.None, stageRequest& NetworkPipelineStage.Requests.Resume); - GeneratePacket(10000, 98, 0xFFFFFFFF, 95, ref sendBuffer, out packet); + GeneratePacket(10000, 98, ~0ul, 95, ref sendBuffer, out packet); // First 95 is received and then receive resume runs up to 99 stageRequest = NetworkPipelineStage.Requests.None; @@ -1135,7 +1135,7 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProper // Verify that the ReceivePackets state is correct, 99 should be latest received and ackmask 0xFFFFF Assert.AreEqual(99, sharedContext->ReceivedPackets.Sequence); - Assert.AreEqual(0xFFFFFFFF, sharedContext->ReceivedPackets.AckMask); + Assert.AreEqual(~0ul, sharedContext->ReceivedPackets.AckedMask); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -1170,9 +1170,9 @@ public unsafe void Send_PacketsAreAcked_SendingPacket() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; sharedContext->SentPackets.Acked = 2; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = 2; - sharedContext->ReceivedPackets.AckMask = 0xFFFFFFFF; + sharedContext->ReceivedPackets.AckedMask = ~0ul; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 1; @@ -1183,7 +1183,7 @@ public unsafe void Send_PacketsAreAcked_SendingPacket() var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafePtr(), staticBuffer.Length, new NetworkSettings()); var stream = new DataStreamWriter(4, Allocator.Temp); - pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); + pipelineContext.header = new DataStreamWriter(ReliableUtility.PacketHeaderWireSize(parameters.WindowSize), Allocator.Temp); { // Fill window capacity, next send should then clear everything stream.Clear(); @@ -1217,10 +1217,10 @@ public unsafe void Send_PacketsAreAcked_SendingPacket() Assert.AreEqual(NetworkPipelineStage.Requests.Update, stageRequest); // Verify ack packet is written correctly - ReliableUtility.PacketHeader header = default; + ReliableUtility.ReliableHeader header = default; ReliableUtility.WriteAckPacket(pipelineContext, ref header); Assert.AreEqual(header.AckedSequenceId, 2); - Assert.AreEqual(header.AckMask, 0xFFFFFFFF); + Assert.AreEqual(header.AckedMask, ~0ul); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -1253,9 +1253,9 @@ public unsafe void Send_PacketsAreAcked_UpdateAckState() var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; sharedContext->SentPackets.Acked = 2; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = 2; - sharedContext->ReceivedPackets.AckMask = 0xFFFFFFFF; + sharedContext->ReceivedPackets.AckedMask = ~0ul; var receiveContext = (ReliableUtility.Context*)recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 1; @@ -1271,7 +1271,7 @@ public unsafe void Send_PacketsAreAcked_UpdateAckState() var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafeReadOnlyPtr(), staticBuffer.Length, new NetworkSettings()); var stream = new DataStreamWriter(4, Allocator.Temp); - pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); + pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); { // Fill window capacity, next send should then clear everything stream.Clear(); @@ -1300,7 +1300,7 @@ public unsafe void Send_PacketsAreAcked_UpdateAckState() sharedBuffer.Dispose(); } - unsafe void GeneratePacket(int payload, ushort headerAckedId, uint headerAckMask, ushort headerSeqId, ref NativeArray sendBuffer, out InboundRecvBuffer packet) + unsafe void GeneratePacket(int payload, ushort headerAckedId, ulong headerAckedMask, ushort headerSeqId, ref NativeArray sendBuffer, out InboundRecvBuffer packet) { DataStreamWriter inboundStream = new DataStreamWriter(4, Allocator.Temp); @@ -1309,10 +1309,10 @@ unsafe void GeneratePacket(int payload, ushort headerAckedId, uint headerAckMask data.bufferWithHeaders = (byte*)inboundStream.AsNativeArray().GetUnsafePtr(); data.bufferWithHeadersLength = inboundStream.Length; data.SetBufferFrombufferWithHeaders(); - ReliableUtility.PacketHeader header = new ReliableUtility.PacketHeader() + ReliableUtility.ReliableHeader header = new ReliableUtility.ReliableHeader() { AckedSequenceId = headerAckedId, - AckMask = headerAckMask, + AckedMask = headerAckedMask, SequenceId = headerSeqId }; ReliableUtility.SetHeaderAndPacket((byte*)sendBuffer.GetUnsafePtr(), headerSeqId, header, data, 1000); @@ -1539,9 +1539,9 @@ public unsafe void NetworkPipeline_ReliableSequenced_SendRecvManyWithPacketDropH var sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = ushort.MaxValue - 1; sharedContext->SentPackets.Acked = ushort.MaxValue - 2; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; var receiveContext = (ReliableUtility.Context*)receiveBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; @@ -1558,9 +1558,9 @@ public unsafe void NetworkPipeline_ReliableSequenced_SendRecvManyWithPacketDropH sharedContext = (ReliableUtility.SharedContext*)sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = ushort.MaxValue - 1; sharedContext->SentPackets.Acked = ushort.MaxValue - 2; - sharedContext->SentPackets.AckMask = 0xFFFFFFFF; + sharedContext->SentPackets.AckedMask = ~0ul; sharedContext->ReceivedPackets.Sequence = sharedContext->SentPackets.Acked; - sharedContext->ReceivedPackets.AckMask = sharedContext->SentPackets.AckMask; + sharedContext->ReceivedPackets.AckedMask = sharedContext->SentPackets.AckedMask; receiveContext = (ReliableUtility.Context*)receiveBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; diff --git a/Tests/Runtime/DisconnectTimeoutTests.cs b/Tests/Runtime/DisconnectTimeoutTests.cs index 98b2b42..cdccecc 100644 --- a/Tests/Runtime/DisconnectTimeoutTests.cs +++ b/Tests/Runtime/DisconnectTimeoutTests.cs @@ -14,6 +14,14 @@ public class DisconnectTimeoutTests // because lag spikes on slower CI machines can lead to the event being generated late. private const long MaxDisconnectTimeMS = DisconnectTimeoutMS + 150; + private static readonly SecureProtocolMode[] s_SecureModeParameters = + { +#if ENABLE_MANAGED_UNITYTLS + SecureProtocolMode.SecureProtocolServerAuthOnly, +#endif + SecureProtocolMode.SecureProtocolDisabled + }; + [Test] public void DisconnectTimeout_ReachedOnCommLoss() { @@ -112,5 +120,26 @@ public void DisconnectTimeout_NotReachedWithFrequentHeartbeats() }); } } + + [Test] + public void DisconnectTimeout_ReachedInSameUpdateAsHeartbeatTimeout( + [ValueSource("s_SecureModeParameters")] SecureProtocolMode secureMode) + { + var settings = new NetworkSettings(); + settings.WithNetworkConfigParameters( + disconnectTimeoutMS: 5, + heartbeatTimeoutMS: 5, + fixedFrameTimeMS: 6); + + using (var server = CreateServer(secureMode, settings)) + using (var client = CreateClient(secureMode, settings)) + { + ConnectServerAndClient(NetworkEndPoint.LoopbackIpv4, server, client, out _, out _); + + client.ScheduleUpdate().Complete(); + + WaitForEvent(NetworkEvent.Type.Disconnect, client); + } + } } } diff --git a/Tests/Runtime/SendMessageTests.cs b/Tests/Runtime/SendMessageTests.cs index 5b8f90e..119d1f8 100644 --- a/Tests/Runtime/SendMessageTests.cs +++ b/Tests/Runtime/SendMessageTests.cs @@ -283,5 +283,109 @@ public void SendMessage_ReceiveAfterConnectionClose([ValueSource("s_SecureModePa }); } } + + [Test] + public void SendMessage_ReliableStressTest() + { + const int TotalPackets = 500; + + for (uint seed = 1; seed <= 5; seed++) + { + Debug.Log($"Testing with random seed {seed}..."); + + var settings = new NetworkSettings(); + settings.WithSimulatorStageParameters( + maxPacketCount: 1000, + maxPacketSize: NetworkParameterConstants.MTU, + packetDelayMs: 0, + packetJitterMs: 5, + packetDropPercentage: 5, + randomSeed: seed); + settings.WithReliableStageParameters(windowSize: 64); + + var dummyData = new NativeArray(1000, Allocator.Temp); + var expectedPacketLength = dummyData.Length + sizeof(int); + + using (var server = CreateServer(SecureProtocolMode.SecureProtocolDisabled, settings, new IPCNetworkInterface())) + using (var client = CreateClient(SecureProtocolMode.SecureProtocolDisabled, settings, new IPCNetworkInterface())) + { + var serverPipeline = server.CreatePipeline(typeof(ReliableSequencedPipelineStage), typeof(SimulatorPipelineStage), typeof(SimulatorPipelineStageInSend)); + var clientPipeline = client.CreatePipeline(typeof(ReliableSequencedPipelineStage), typeof(SimulatorPipelineStage), typeof(SimulatorPipelineStageInSend)); + + ConnectServerAndClient(NetworkEndPoint.LoopbackIpv4, server, client, out var s2cConnection, out var c2sConnection); + + var numServerDataEvents = 0; + var numClientDataEvents = 0; + + var numServerPacketsSent = 0; + var numClientPacketsSent = 0; + + WaitForCondition(() => + { + while (numServerPacketsSent < TotalPackets) + { + Assert.AreEqual(0, server.BeginSend(serverPipeline, s2cConnection, out var serverWriter)); + serverWriter.WriteInt(numServerPacketsSent); + serverWriter.WriteBytes(dummyData); + + var result = server.EndSend(serverWriter); + if (result != expectedPacketLength) + { + Assert.AreEqual((int)Error.StatusCode.NetworkSendQueueFull, result); + break; + } + numServerPacketsSent++; + } + + while (numClientPacketsSent < TotalPackets) + { + Assert.AreEqual(0, client.BeginSend(clientPipeline, c2sConnection, out var clientWriter)); + clientWriter.WriteInt(numClientPacketsSent + 42000); + clientWriter.WriteBytes(dummyData); + + var result = client.EndSend(clientWriter); + if (result != expectedPacketLength) + { + Assert.AreEqual((int)Error.StatusCode.NetworkSendQueueFull, result); + break; + } + numClientPacketsSent++; + } + + server.ScheduleUpdate().Complete(); + client.ScheduleUpdate().Complete(); + + NetworkEvent.Type ev; + NetworkConnection connection; + DataStreamReader reader; + NetworkPipeline pipeline; + + while ((ev = server.PopEvent(out connection, out reader, out pipeline)) != NetworkEvent.Type.Empty) + { + Assert.AreEqual(NetworkEvent.Type.Data, ev); + Assert.AreEqual(s2cConnection, connection); + Assert.AreEqual(serverPipeline, pipeline); + Assert.AreEqual(expectedPacketLength, reader.Length); + Assert.AreEqual(numServerDataEvents + 42000, reader.ReadInt()); + + numServerDataEvents++; + } + + while ((ev = client.PopEvent(out connection, out reader, out pipeline)) != NetworkEvent.Type.Empty) + { + Assert.AreEqual(NetworkEvent.Type.Data, ev); + Assert.AreEqual(c2sConnection, connection); + Assert.AreEqual(clientPipeline, pipeline); + Assert.AreEqual(expectedPacketLength, reader.Length); + Assert.AreEqual(numClientDataEvents, reader.ReadInt()); + + numClientDataEvents++; + } + + return numServerDataEvents == TotalPackets && numClientDataEvents == TotalPackets; + }, "Timed out waiting for all reliable packets.", 10000); + } + } + } } } diff --git a/Tests/Runtime/Utilities/SecureTestParameters.cs b/Tests/Runtime/Utilities/SecureTestParameters.cs index 8fbbeb7..5e62cb8 100644 --- a/Tests/Runtime/Utilities/SecureTestParameters.cs +++ b/Tests/Runtime/Utilities/SecureTestParameters.cs @@ -11,123 +11,128 @@ public static class SecureTestParameters public static FixedString4096Bytes CaCertificate = new FixedString4096Bytes( @"-----BEGIN CERTIFICATE----- -MIIDDzCCAfcCFBU2SZ+6r8bdx36uGkHroQpEmX+lMA0GCSqGSIb3DQEBCwUAMEQx -CzAJBgNVBAYTAkNBMQ8wDQYDVQQIDAZRdWViZWMxETAPBgNVBAcMCE1vbnRyZWFs -MREwDwYDVQQKDAhVbml0eSBDQTAeFw0yMjAzMTcwMzQyNDFaFw0zMjAzMTQwMzQy -NDFaMEQxCzAJBgNVBAYTAkNBMQ8wDQYDVQQIDAZRdWViZWMxETAPBgNVBAcMCE1v -bnRyZWFsMREwDwYDVQQKDAhVbml0eSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBALv/mWjDrxtKTqKRrNBqZ9an0m60tSSNaXX9BRSOyGuqFmdEdW5v -YnQDXsn9wGKFF6mgr2ATfgL273Im95aLRvHwhNmEP2c2T6WUq//Pq32nJ8kwiKly -2ctBdp6QyxgRuKMvFhTFAjzEdwH6GNWdmDjq3BgErKH8JhBnAzV5DAdbnr0pC4es -0ZAOVw8iyxKWW9U6/pb/Jed6R/ioV6OuGQbaAfGhFO2/lt3RYI4MkUr9pTWIqJwc -aDL9WxCoTggVNkckQmlMiLe0rYcCqEc+A0MdWblVNKds6HcBxyjMgxsELA4DmQ7C -4frNN8EtokxbaqjbM/cJNYfQ9IoBsATKaHkCAwEAATANBgkqhkiG9w0BAQsFAAOC -AQEAB7FwMBsB+pU6VBBGJPrHm70RitGffyDTefDtSOyrNXxdHyoMiSFhb26w/iin -/jubAZ5I3lvNFawRrDlzlJSxJDjaiHDd29W5UcV+6ij3Te/NJhck+9tXfuy6r95+ -jjgGpm1RvBQq5XhEJh5FMfzXUYZ6NFg+6fLfqbE/hHo2mq+S0AAwR6gwDpr/6UzU -bARuY+bmrEFjEVFXNkmv4iZDkMQTi8UbmiwsNX3zJBPmSCErKiIPLHXBpzJitmcG -VYgO3hp/EObkBLHheqUuqLIY6XDvDhVPiJq4VyNGHnhR6GSiXs4ixL6v+UWrCHbh -ud3r5a40pzFbEWb6Zzrb3+BQZQ== +MIIDpzCCAo+gAwIBAgIUeyWOu7GQSV0N3dZNwRr2Pl8HXPAwDQYJKoZIhvcNAQEL +BQAwYjELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzERMA8GA1UEBwwITW9u +dHJlYWwxGzAZBgNVBAoMElVuaXR5IFRlY2hub2xvZ2llczESMBAGA1UEAwwJMTI3 +LjAuMC4xMCAXDTIzMDMxNzE4MDk0MloYDzMwMjIwNzE4MTgwOTQyWjBiMQswCQYD +VQQGEwJDQTEPMA0GA1UECAwGUXVlYmVjMREwDwYDVQQHDAhNb250cmVhbDEbMBkG +A1UECgwSVW5pdHkgVGVjaG5vbG9naWVzMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD8ikJud244RTt3tCoBluJxocUw +QboPkWU6FHGPiqztACk5ergbd3zvd2//daM7HVGy857vDoZZ9PvPSP29AvD3eO8v +KFoBWfeTzGjXw0L5YXQ3wxq1fhJ1BOI0XLRVPndhLrBLsETz0XUctZASC/EfPiQD +m5gINh7HcQkwza7z7XfX9+A5ttzFHFntoLOFCL67H6iAEGntj4LX/zlZSnE+1F2L +wTYrQAja4XXvJH2GLCYhBiqYbYuaigKfOQvDXCQWCNeyfD/Xh2ugzBXkMOl+ngV6 +Ei7qj2sNWWZh49fmTz9DDjh9Jf3gISpcOjabIpE7ZWYuDwhV8YhssK5fnfrdAgMB +AAGjUzBRMB0GA1UdDgQWBBS1loTCqrnX8gnj/TPUzEwIq8lgWzAfBgNVHSMEGDAW +gBS1loTCqrnX8gnj/TPUzEwIq8lgWzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQCi+A4Fm4HTbL3rjtDM0mXeqjD+XpnxzmsYmSqUXLaDw4OkTQZG +QcBwWaP8GhbhcCgNdd5wNsF8zi8B8KN2ApuefOy9VJCVzq+ZNVde8ib1BtRRogng +U81Kv5Cah2la16wj7Vq1B4SAbKA7mBtagyF4kOU52W93RmYrXZw1LETK5fDCXmUA +/ddDuls7dwbsdoZSGv1UouMe/u/JU5v5M2L9naXj3ajdrPe4MNDSeYc+kMp4qSmd +02falAMqS4vEdqcems1cLzLNjOnbdz0kjRlC1THNsKbyAC7s3icPC7Sp6eByHI0e +M4VrSMAGTfD7PRdBxHYRKRCvstrx1SINX10u -----END CERTIFICATE-----"); public static FixedString4096Bytes Certificate1 = new FixedString4096Bytes( @"-----BEGIN CERTIFICATE----- -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 -----END CERTIFICATE-----"); public static FixedString4096Bytes PrivateKey1 = new FixedString4096Bytes( @"-----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY-----"); public static FixedString4096Bytes Certificate2 = new FixedString4096Bytes( @"-----BEGIN CERTIFICATE----- -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 -----END CERTIFICATE-----"); public static FixedString4096Bytes PrivateKey2 = new FixedString4096Bytes( @"-----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY-----"); } } diff --git a/ValidationExceptions.json b/ValidationExceptions.json index fd41e18..cb627b4 100644 --- a/ValidationExceptions.json +++ b/ValidationExceptions.json @@ -3,12 +3,27 @@ { "ValidationTest": "Restricted File Type Validation", "ExceptionMessage": "/Samples~/CustomNetworkInterface/Scripts/network.bindings~/build.bat cannot be included in a package.", - "PackageVersion": "1.3.0" + "PackageVersion": "1.3.3" }, { "ValidationTest": "Restricted File Type Validation", "ExceptionMessage": "/Samples~/CustomNetworkInterface/Scripts/network.bindings~/shell.bat cannot be included in a package.", - "PackageVersion": "1.3.0" + "PackageVersion": "1.3.3" + }, + { + "ValidationTest": "API Validation", + "ExceptionMessage": "Additions require a new minor or major version.", + "PackageVersion": "1.3.3" + }, + { + "ValidationTest": "API Validation", + "ExceptionMessage": "New assembly \"Unity.Networking.Transport.RuntimeTests\" may only be added in a new minor or major version.", + "PackageVersion": "1.3.3" + }, + { + "ValidationTest": "API Validation", + "ExceptionMessage": "New assembly \"Unity.Networking.Transport.EditorTests\" may only be added in a new minor or major version.", + "PackageVersion": "1.3.3" } ], "WarningExceptions": [] diff --git a/package.json b/package.json index 16eddb6..0b4d29a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.transport", "displayName": "Unity Transport", - "version": "1.3.0", + "version": "1.3.3", "unity": "2020.3", "unityRelease": "0f1", "description": "Unity network transport layer - the low-level interface for sending UDP data", @@ -10,13 +10,17 @@ "com.unity.burst": "1.6.6", "com.unity.mathematics": "1.2.6" }, + "_upm": { + "changelog": "### Fixes\n* Fixed an issue where calling `ScheduleFlushSend` before the socket was bound would still result in socket system calls being made, resulting in errors being logged." + }, "upmCi": { - "footprint": "31f22ba11171691d997674e0ee95fb20c0442cd3" + "footprint": "45bfeaef19c4c8984cb161a101b0d5d39c3e154d" }, + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.transport@1.3/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/com.unity.transport.git", "type": "git", - "revision": "6ca6b799abad242031e9148bebeee95d74fb5df2" + "revision": "705fec881f66f56109ff57cc62cf2fba6b87e73d" }, "samples": [ {