diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a665a..29881ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change log +## [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 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/IPCNetworkInterface.cs b/Runtime/IPCNetworkInterface.cs index 129b7aa..8b6ae5a 100644 --- a/Runtime/IPCNetworkInterface.cs +++ b/Runtime/IPCNetworkInterface.cs @@ -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/NetworkDriver.cs b/Runtime/NetworkDriver.cs index 5c07e8b..d31c00c 100644 --- a/Runtime/NetworkDriver.cs +++ b/Runtime/NetworkDriver.cs @@ -226,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; @@ -235,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; @@ -364,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; } diff --git a/Runtime/Pipelines/ReliableUtility.cs b/Runtime/Pipelines/ReliableUtility.cs index 3bcc178..67091cd 100644 --- a/Runtime/Pipelines/ReliableUtility.cs +++ b/Runtime/Pipelines/ReliableUtility.cs @@ -634,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; @@ -902,7 +902,7 @@ internal static unsafe int Read(NetworkPipelineContext context, ReliableHeader h } 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); @@ -927,7 +927,7 @@ internal static unsafe int Read(NetworkPipelineContext context, ReliableHeader h 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 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/ValidationExceptions.json b/ValidationExceptions.json index 83b35f6..35a3c23 100644 --- a/ValidationExceptions.json +++ b/ValidationExceptions.json @@ -3,27 +3,27 @@ { "ValidationTest": "Restricted File Type Validation", "ExceptionMessage": "/Samples~/CustomNetworkInterface/Scripts/network.bindings~/build.bat cannot be included in a package.", - "PackageVersion": "1.3.1" + "PackageVersion": "1.3.2" }, { "ValidationTest": "Restricted File Type Validation", "ExceptionMessage": "/Samples~/CustomNetworkInterface/Scripts/network.bindings~/shell.bat cannot be included in a package.", - "PackageVersion": "1.3.1" + "PackageVersion": "1.3.2" }, { "ValidationTest": "API Validation", "ExceptionMessage": "Additions require a new minor or major version.", - "PackageVersion": "1.3.1" + "PackageVersion": "1.3.2" }, { "ValidationTest": "API Validation", "ExceptionMessage": "New assembly \"Unity.Networking.Transport.RuntimeTests\" may only be added in a new minor or major version.", - "PackageVersion": "1.3.1" + "PackageVersion": "1.3.2" }, { "ValidationTest": "API Validation", "ExceptionMessage": "New assembly \"Unity.Networking.Transport.EditorTests\" may only be added in a new minor or major version.", - "PackageVersion": "1.3.1" + "PackageVersion": "1.3.2" } ], "WarningExceptions": [] diff --git a/package.json b/package.json index d478382..9b79fbd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.transport", "displayName": "Unity Transport", - "version": "1.3.1", + "version": "1.3.2", "unity": "2020.3", "unityRelease": "0f1", "description": "Unity network transport layer - the low-level interface for sending UDP data", @@ -11,16 +11,16 @@ "com.unity.mathematics": "1.2.6" }, "_upm": { - "changelog": "### Changes\n* 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.\n\n### Fixes\n* 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.\n* Fixed a crash when using DTLS if an update was delayed for long enough that both the disconnection and heartbeat timeouts expire." + "changelog": "### Fixes\n* Fixed issue where UWP Xbox builds were crashing because the split buffer fix was not including UWP defines.\n* Fixed an issue where `IPCNetworkInterface` would perform useless work for each packet received.\n* Fixed an issue where `ReliableSequencedPipelineStage` could end up duplicating packets when sending reliable packets while the send queue is full." }, "upmCi": { - "footprint": "d5689bea7cfc3808f75e8306def417c52e04f307" + "footprint": "98182eeaea2fd3b091f4a2b16b9c9471598db23b" }, "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": "80ac568c7067b2af506786cefbe8a71e4025d032" + "revision": "21ae74bd7af3124f937100ff2cf8d2130fcd9307" }, "samples": [ {