Skip to content

Commit 6b8f88b

Browse files
noellie-velezEmandMNoelStephensUnity
authored
fix: cap maximum disconnect timeout (#3810)
* chore: avoid overflow when calculating maxCapacity * Changelog update * Add test * chore: handle the case of the disconnect timeout being 0 * add tests for disconnectTimout set to 0 * changelog update --------- Co-authored-by: Emma <[email protected]> Co-authored-by: Noel Stephens <[email protected]>
1 parent 3233d4b commit 6b8f88b

File tree

5 files changed

+107
-5
lines changed

5 files changed

+107
-5
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
4242
### Changed
4343

4444
- Improve performance of `ParentSyncMessage`. (#3814)
45+
- If the Unity Transport Disconnect Timeout is set to 0 in the Editor, the timeout will be entirely disabeled. (#3810)
4546
- Improve performance of `DestroyObjectMessage`. (#3801)
4647
- Improve performance of `CreateObjectMessage`. (#3800)
4748
- First pass of CoreCLR engine API changes. (#3799)
@@ -53,6 +54,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
5354

5455
- Ensure `NetworkBehaviour.IsSessionOwner` is correctly set when a new session owner is promoted. (#3817)
5556
- Reset extended ownership flags on `NetworkObject` despawn. (#3817)
57+
- Fixed issue where maxCapacity calculation overflows if a developer sets a very, very high (large) m_DisconnectTimeoutMS in the Editor for Unity Transport. (#3810)
5658
- Fixed issues with the "Client-server quickstart for Netcode for GameObjects" script having static methods and properties. (#3787)
5759
- Fixed issue where a warning message was being logged upon a client disconnecting from a server when the log level is set to developer. (#3786)
5860
- Fixed issue where the server or host would no longer have access to the transport id to client id table when processing a transport level client disconnect event. (#3786)

com.unity.netcode.gameobjects/Runtime/Transports/UTP/BatchedSendQueue.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ internal struct BatchedSendQueue : IDisposable
3131
public const int PerMessageOverhead = sizeof(int);
3232

3333
internal const int MinimumMinimumCapacity = 4096;
34+
// int.MaxValue is odd and maximum must be even.
35+
// This is safe to be so large as the queue is dynamically allocated.
36+
// It's very unlikely that the actual send queue will ever grow this large
37+
internal const int MaximumMaximumCapacity = int.MaxValue - 1;
3438

3539
// Indices into m_HeadTailIndicies.
3640
private const int k_HeadInternalIndex = 0;

com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1515,7 +1515,22 @@ public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDel
15151515
// the only case where a full send queue causes a connection loss. Full unreliable
15161516
// send queues are dealt with by flushing it out to the network or simply dropping
15171517
// new messages if that fails.
1518-
var maxCapacity = m_MaxSendQueueSize > 0 ? m_MaxSendQueueSize : m_DisconnectTimeoutMS * k_MaxReliableThroughput;
1518+
var maxCapacity = m_MaxSendQueueSize;
1519+
if (maxCapacity <= 0)
1520+
{
1521+
// Setting m_DisconnectTimeoutMS to zero will disable the timeout entirely
1522+
// Set the capacity as if the disconnect timeout is the largest possible value
1523+
if (m_DisconnectTimeoutMS == 0)
1524+
{
1525+
maxCapacity = BatchedSendQueue.MaximumMaximumCapacity;
1526+
}
1527+
else
1528+
{
1529+
// Avoids overflow when m_DisconnectTimeoutMS is set to a very high value
1530+
var fullCalculation = Math.BigMul(m_DisconnectTimeoutMS, k_MaxReliableThroughput);
1531+
maxCapacity = (int)Math.Min(fullCalculation, BatchedSendQueue.MaximumMaximumCapacity);
1532+
}
1533+
}
15191534

15201535
queue = new BatchedSendQueue(Math.Max(maxCapacity, m_MaxPayloadSize));
15211536
m_SendQueue.Add(sendTarget, queue);

com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTestHelpers.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,16 @@ public static IEnumerator EnsureNoNetworkEvent(List<TransportEvent> events, floa
9696
}
9797

9898
// Common code to initialize a UnityTransport that logs its events.
99-
public static void InitializeTransport(out UnityTransport transport, out List<TransportEvent> events, int maxPayloadSize = UnityTransport.InitialMaxPayloadSize, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4)
99+
public static void InitializeTransport(out UnityTransport transport, out List<TransportEvent> events, int maxPayloadSize = UnityTransport.InitialMaxPayloadSize, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4, int disconnectTimeout = NetworkParameterConstants.DisconnectTimeoutMS)
100100
{
101-
InitializeTransport(out transport, out events, string.Empty, maxPayloadSize, maxSendQueueSize, family);
101+
InitializeTransport(out transport, out events, string.Empty, maxPayloadSize, maxSendQueueSize, family, disconnectTimeout);
102102
}
103103

104104
/// <summary>
105-
/// Interanl version with identifier parameter
105+
/// Internal version with identifier parameter
106106
/// </summary>
107107
internal static void InitializeTransport(out UnityTransport transport, out List<TransportEvent> events, string identifier,
108-
int maxPayloadSize = UnityTransport.InitialMaxPayloadSize, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4)
108+
int maxPayloadSize = UnityTransport.InitialMaxPayloadSize, int maxSendQueueSize = 0, NetworkFamily family = NetworkFamily.Ipv4, int disconnectTimeout = NetworkParameterConstants.DisconnectTimeoutMS)
109109
{
110110
var logger = new TransportEventLogger()
111111
{
@@ -118,6 +118,7 @@ internal static void InitializeTransport(out UnityTransport transport, out List<
118118
transport.OnTransportEvent += logger.HandleEvent;
119119
transport.MaxPayloadSize = maxPayloadSize;
120120
transport.MaxSendQueueSize = maxSendQueueSize;
121+
transport.DisconnectTimeoutMS = disconnectTimeout;
121122

122123
if (family == NetworkFamily.Ipv6)
123124
{

com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,86 @@ public IEnumerator SendMaximumPayloadSize(
189189
yield return null;
190190
}
191191

192+
[UnityTest]
193+
public IEnumerator VeryLargeDisconnectTimeout()
194+
{
195+
// The calculation will never be lower than the UnityTransport.InitialMaxPayloadSize
196+
// We want to send a message larger than that size to ensure the maximum is high enough
197+
var payloadSize = UnityTransport.InitialMaxPayloadSize * 2;
198+
199+
var disconnectTimeout = int.MaxValue;
200+
201+
InitializeTransport(out m_Server, out m_ServerEvents, payloadSize, disconnectTimeout: disconnectTimeout);
202+
InitializeTransport(out m_Client1, out m_Client1Events, payloadSize, disconnectTimeout: disconnectTimeout);
203+
Assert.That(m_Server.DisconnectTimeoutMS, Is.EqualTo(disconnectTimeout));
204+
Assert.That(m_Client1.DisconnectTimeoutMS, Is.EqualTo(disconnectTimeout));
205+
206+
m_Server.StartServer();
207+
m_Client1.StartClient();
208+
209+
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
210+
211+
var payloadData = new byte[payloadSize];
212+
for (int i = 0; i < payloadData.Length; i++)
213+
{
214+
payloadData[i] = (byte)i;
215+
}
216+
217+
var payload = new ArraySegment<byte>(payloadData);
218+
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.Reliable);
219+
220+
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents, MaxNetworkEventWaitTime * 4);
221+
222+
Assert.AreEqual(payloadSize, m_ServerEvents[1].Data.Count);
223+
224+
var receivedArray = m_ServerEvents[1].Data.Array;
225+
var receivedArrayOffset = m_ServerEvents[1].Data.Offset;
226+
for (int i = 0; i < payloadSize; i++)
227+
{
228+
Assert.AreEqual(payloadData[i], receivedArray[receivedArrayOffset + i]);
229+
}
230+
}
231+
232+
[UnityTest]
233+
public IEnumerator ZeroDisconnectTimeoutSetToZero()
234+
{
235+
// The calculation will never be lower than the UnityTransport.InitialMaxPayloadSize
236+
// We want to send a message larger than that size to ensure the maximum is high enough
237+
var payloadSize = UnityTransport.InitialMaxPayloadSize * 2;
238+
239+
var disconnectTimeout = 0;
240+
241+
InitializeTransport(out m_Server, out m_ServerEvents, payloadSize, disconnectTimeout: disconnectTimeout);
242+
InitializeTransport(out m_Client1, out m_Client1Events, payloadSize, disconnectTimeout: disconnectTimeout);
243+
Assert.That(m_Server.DisconnectTimeoutMS, Is.EqualTo(disconnectTimeout));
244+
Assert.That(m_Client1.DisconnectTimeoutMS, Is.EqualTo(disconnectTimeout));
245+
246+
m_Server.StartServer();
247+
m_Client1.StartClient();
248+
249+
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
250+
251+
var payloadData = new byte[payloadSize];
252+
for (int i = 0; i < payloadData.Length; i++)
253+
{
254+
payloadData[i] = (byte)i;
255+
}
256+
257+
var payload = new ArraySegment<byte>(payloadData);
258+
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.Reliable);
259+
260+
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents, MaxNetworkEventWaitTime * 4);
261+
262+
Assert.AreEqual(payloadSize, m_ServerEvents[1].Data.Count);
263+
264+
var receivedArray = m_ServerEvents[1].Data.Array;
265+
var receivedArrayOffset = m_ServerEvents[1].Data.Offset;
266+
for (int i = 0; i < payloadSize; i++)
267+
{
268+
Assert.AreEqual(payloadData[i], receivedArray[receivedArrayOffset + i]);
269+
}
270+
}
271+
192272
// Check making multiple sends to a client in a single frame.
193273
[UnityTest]
194274
public IEnumerator MultipleSendsSingleFrame(

0 commit comments

Comments
 (0)