Skip to content

Commit b9c836a

Browse files
fix: Sends failing on UTP adapter when queue is full (#1317)
* fix: Sends failing on UTP adapter when queue is full Or close to full. There were two issues here. First, the payload capacity of the fragmentation pipeline didn't account for the entire size of the packet. Unfortunately, payload capacity in the context of the fragmentation pipeline is a bit of a misnomer since it also includes headers and other such overhead. We were setting it to the size of a full send queue, and so when you added UTP headers, we ended up with a packet too large for the pipeline. Bumping up the value a bit solves this issue. Second, when sending a single message that was within 4 bytes of the send queue capacity, we'd silently fail to send it. The reason was we didn't account for the extra overhead taken by the data length in the send queue (which requires 4 bytes). Thus the send code would think the message could fit in the queue when it couldn't. Fixing this simply required accounting for the 4 extra bytes in the send logic. Two tests were also added to test sends when the send queue is full (either from one big send or from multiple smaller ones in a row). Both tests fail without the fixes in the adapter. * Cleaner fix for the send queue being full scenario This one depends on a fix in UTP that's still being reviewed, though. * Fix possible re-ordering issue for large messages * Reword the warning when sending immediately Events can't be larger than m_SendQueueBatchSize. UTP will drop them and log an error in the current configuration. Reword the warning so that we don't imply that it's been sent, only that we're trying to send it. If it fails, there will be an error in the log following it, making it clear to the user what happened. * Update UTP dependency to 1.0.0-pre.7 * fix: Fixing validation error Co-authored-by: Andrew Spiering <andrews@unity3d.com>
1 parent 5fcb793 commit b9c836a

File tree

4 files changed

+77
-19
lines changed

4 files changed

+77
-19
lines changed

com.unity.netcode.adapter.utp/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# Changelog
22
All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
33

4+
## [1.0.0-pre.3] - 2021-10-22
5+
6+
### Changed
7+
8+
- Updated Unity Transport package to 1.0.0-pre.7
9+
10+
### Fixed
11+
12+
- Fixed sends failing when send queue is filled or close to be filled.
13+
414
## [1.0.0-pre.2] - 2020-12-20
515

616
### Changed

com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -563,10 +563,13 @@ public override void Initialize()
563563

564564
m_NetworkParameters = new List<INetworkParameter>();
565565

566-
// If we want to be able to actually handle messages MaximumMessageLength bytes in
567-
// size, we need to allow a bit more than that in FragmentationUtility since this needs
568-
// to account for headers and such. 128 bytes is plenty enough for such overhead.
569-
m_NetworkParameters.Add(new FragmentationUtility.Parameters() { PayloadCapacity = m_SendQueueBatchSize });
566+
// If the user sends a message of exactly m_SendQueueBatchSize length, we'll need an
567+
// extra byte to mark it as non-batched and 4 bytes for its length. If the user fills
568+
// up the send queue to its capacity (batched messages total m_SendQueueBatchSize), we
569+
// still need one extra byte to mark the payload as batched.
570+
var fragmentationCapacity = m_SendQueueBatchSize + 1 + 4;
571+
m_NetworkParameters.Add(new FragmentationUtility.Parameters() { PayloadCapacity = fragmentationCapacity });
572+
570573
m_NetworkParameters.Add(new BaselibNetworkParameter()
571574
{
572575
maximumPayloadSize = (uint)m_MaximumPacketSize,
@@ -596,24 +599,22 @@ public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDel
596599
}
597600

598601
var success = queue.AddEvent(payload);
599-
if (!success) // This would be false only when the SendQueue is full already or we are sending a super large message at once
602+
if (!success) // No more room in the send queue for the message.
600603
{
601-
// If we are in here data exceeded remaining queue size. This should not happen under normal operation.
602-
if (payload.Count > queue.Size)
604+
// Flushing the send queue ensures we preserve the order of sends.
605+
SendBatchedMessageAndClearQueue(sendTarget, queue);
606+
Debug.Assert(queue.IsEmpty() == true);
607+
queue.Clear();
608+
609+
// Try add the message to the queue as there might be enough room now that it's empty.
610+
success = queue.AddEvent(payload);
611+
if (!success) // Message is too large to fit in the queue. Shouldn't happen under normal operation.
603612
{
604613
// If data is too large to be batched, flush it out immediately. This happens with large initial spawn packets from Netcode for Gameobjects.
605-
Debug.LogWarning($"Sent {payload.Count} bytes based on delivery method: {networkDelivery}. Event size exceeds sendQueueBatchSize: ({m_SendQueueBatchSize}). This can be the initial payload!");
614+
Debug.LogWarning($"Event of size {payload.Count} too large to fit in send queue (of size {m_SendQueueBatchSize}). Trying to send directly. This could be the initial payload!");
606615
Debug.Assert(networkDelivery == NetworkDelivery.ReliableFragmentedSequenced); // Messages like this, should always be sent via the fragmented pipeline.
607616
SendMessageInstantly(sendTarget.ClientId, payload, pipeline);
608617
}
609-
else
610-
{
611-
// Since our queue buffer is full then send that right away, clear it and queue this new data
612-
SendBatchedMessageAndClearQueue(sendTarget, queue);
613-
Debug.Assert(queue.IsEmpty() == true);
614-
queue.Clear();
615-
queue.AddEvent(payload);
616-
}
617618
}
618619
}
619620

com.unity.netcode.adapter.utp/Tests/Runtime/TransportTests.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ public IEnumerator PingPong()
7676
yield return null;
7777
}
7878

79-
80-
8179
// Check if can make a simple data exchange (both ways at a time).
8280
[UnityTest]
8381
public IEnumerator PingPongSimultaneous()
@@ -113,6 +111,55 @@ public IEnumerator PingPongSimultaneous()
113111
yield return null;
114112
}
115113

114+
[UnityTest]
115+
public IEnumerator FilledSendQueueSingleSend()
116+
{
117+
InitializeTransport(out m_Server, out m_ServerEvents);
118+
InitializeTransport(out m_Client1, out m_Client1Events);
119+
120+
m_Server.StartServer();
121+
m_Client1.StartClient();
122+
123+
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ServerEvents);
124+
125+
var payload = new ArraySegment<byte>(new byte[UnityTransport.InitialBatchQueueSize]);
126+
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.ReliableFragmentedSequenced);
127+
128+
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
129+
130+
yield return null;
131+
}
132+
133+
[UnityTest]
134+
public IEnumerator FilledSendQueueMultipleSends()
135+
{
136+
InitializeTransport(out m_Server, out m_ServerEvents);
137+
InitializeTransport(out m_Client1, out m_Client1Events);
138+
139+
m_Server.StartServer();
140+
m_Client1.StartClient();
141+
142+
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ServerEvents);
143+
144+
var numSends = UnityTransport.InitialBatchQueueSize / 1024;
145+
146+
for (int i = 0; i < numSends; i++)
147+
{
148+
// We remove 4 bytes because each send carries a 4 bytes overhead in the send queue.
149+
// Without that we wouldn't fill the send queue; it would get flushed right when we
150+
// try to send the last message.
151+
var payload = new ArraySegment<byte>(new byte[1024 - 4]);
152+
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.ReliableFragmentedSequenced);
153+
}
154+
155+
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
156+
157+
// Extra event is for the Connect event.
158+
Assert.AreEqual(numSends + 1, m_ServerEvents.Count);
159+
160+
yield return null;
161+
}
162+
116163
// Check making multiple sends to a client in a single frame.
117164
[UnityTest]
118165
public IEnumerator MultipleSendsSingleFrame()

com.unity.netcode.adapter.utp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"unity": "2020.3",
77
"dependencies": {
88
"com.unity.netcode.gameobjects": "1.0.0-pre.2",
9-
"com.unity.transport": "1.0.0-pre.6"
9+
"com.unity.transport": "1.0.0-pre.7"
1010
}
1111
}

0 commit comments

Comments
 (0)