Skip to content

Commit a981703

Browse files
authored
feat: per-peer MTU (#2676)
1 parent f890d7e commit a981703

File tree

4 files changed

+115
-3
lines changed

4 files changed

+115
-3
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
77
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
88

99
## [Unreleased]
10+
1011
### Added
1112

13+
- Added methods NetworkManager.SetPeerMTU and NetworkManager.GetPeerMTU to be able to set MTU sizes per-peer (#2676)
14+
1215
### Fixed
1316

1417
- Fixed issue where `SpawnWithObservers` was not being honored when `NetworkConfig.EnableSceneManagement` was disabled. (#2682)

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,12 @@ private void OnEnable()
581581

582582
/// <summary>
583583
/// Sets the maximum size of a single non-fragmented message (or message batch) passed through the transport.
584-
/// This should represent the transport's MTU size, minus any transport-level overhead.
584+
/// This should represent the transport's default MTU size, minus any transport-level overhead.
585+
/// This value will be used for any remote endpoints that haven't had per-endpoint MTUs set.
586+
/// This value is also used as the size of the temporary buffer used when serializing
587+
/// a single message (to avoid serializing multiple times when sending to multiple endpoints),
588+
/// and thus should be large enough to ensure it can hold each message type.
589+
/// This value defaults to 1296.
585590
/// </summary>
586591
/// <param name="size"></param>
587592
public int MaximumTransmissionUnitSize
@@ -590,6 +595,34 @@ public int MaximumTransmissionUnitSize
590595
get => MessageManager.NonFragmentedMessageMaxSize;
591596
}
592597

598+
/// <summary>
599+
/// Set the maximum transmission unit for a specific peer.
600+
/// This determines the maximum size of a message batch that can be sent to that client.
601+
/// If not set for any given client, <see cref="MaximumTransmissionUnitSize"/> will be used instead.
602+
/// </summary>
603+
/// <param name="clientId"></param>
604+
/// <param name="size"></param>
605+
public void SetPeerMTU(ulong clientId, int size)
606+
{
607+
MessageManager.PeerMTUSizes[clientId] = size;
608+
}
609+
610+
/// <summary>
611+
/// Queries the current MTU size for a client.
612+
/// If no MTU has been set for that client, will return <see cref="MaximumTransmissionUnitSize"/>
613+
/// </summary>
614+
/// <param name="clientId"></param>
615+
/// <returns></returns>
616+
public int GetPeerMTU(ulong clientId)
617+
{
618+
if (MessageManager.PeerMTUSizes.TryGetValue(clientId, out var ret))
619+
{
620+
return ret;
621+
}
622+
623+
return MessageManager.NonFragmentedMessageMaxSize;
624+
}
625+
593626
/// <summary>
594627
/// Sets the maximum size of a message (or message batch) passed through the transport with the ReliableFragmented delivery.
595628
/// Warning: setting this value too low may result in the SDK becoming non-functional with projects that have a large number of NetworkBehaviours or NetworkVariables, as the SDK relies on the transport's ability to fragment some messages when they grow beyond the MTU size.

com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ internal uint GetMessageType(Type t)
9999
public int NonFragmentedMessageMaxSize = DefaultNonFragmentedMessageMaxSize;
100100
public int FragmentedMessageMaxSize = int.MaxValue;
101101

102+
public Dictionary<ulong, int> PeerMTUSizes = new Dictionary<ulong, int>();
103+
102104
internal struct MessageWithHandler
103105
{
104106
public Type MessageType;
@@ -497,6 +499,7 @@ private void CleanupDisconnectedClient(ulong clientId)
497499
m_SendQueues.Remove(clientId);
498500

499501
m_PerClientMessageVersions.Remove(clientId);
502+
PeerMTUSizes.Remove(clientId);
500503
}
501504

502505
internal void CleanupDisconnectedClients()
@@ -678,6 +681,21 @@ internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter t
678681
continue;
679682
}
680683

684+
var startSize = NonFragmentedMessageMaxSize;
685+
if (delivery != NetworkDelivery.ReliableFragmentedSequenced)
686+
{
687+
if (PeerMTUSizes.TryGetValue(clientId, out var clientMaxSize))
688+
{
689+
maxSize = clientMaxSize;
690+
}
691+
startSize = maxSize;
692+
if (tmpSerializer.Position >= maxSize)
693+
{
694+
Debug.LogError($"MTU size for {clientId} is too small to contain a message of type {typeof(TMessageType).FullName}");
695+
continue;
696+
}
697+
}
698+
681699
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
682700
{
683701
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, ref message, delivery);
@@ -686,15 +704,15 @@ internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter t
686704
var sendQueueItem = m_SendQueues[clientId];
687705
if (sendQueueItem.Length == 0)
688706
{
689-
sendQueueItem.Add(new SendQueueItem(delivery, NonFragmentedMessageMaxSize, Allocator.TempJob, maxSize));
707+
sendQueueItem.Add(new SendQueueItem(delivery, startSize, Allocator.TempJob, maxSize));
690708
sendQueueItem.ElementAt(0).Writer.Seek(sizeof(NetworkBatchHeader));
691709
}
692710
else
693711
{
694712
ref var lastQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
695713
if (lastQueueItem.NetworkDelivery != delivery || lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position < tmpSerializer.Length + headerSerializer.Length)
696714
{
697-
sendQueueItem.Add(new SendQueueItem(delivery, NonFragmentedMessageMaxSize, Allocator.TempJob, maxSize));
715+
sendQueueItem.Add(new SendQueueItem(delivery, startSize, Allocator.TempJob, maxSize));
698716
sendQueueItem.ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(NetworkBatchHeader));
699717
}
700718
}

com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,64 @@ public void WhenExceedingBatchSize_NewBatchesAreCreated([Values(500, 1000, 1300,
181181
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
182182
}
183183

184+
[Test]
185+
public void WhenExceedingPerClientBatchSizeLessThanDefault_NewBatchesAreCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
186+
{
187+
var message = GetMessage();
188+
m_MessageManager.NonFragmentedMessageMaxSize = maxMessageSize * 5;
189+
var clients = new ulong[] { 0, 1, 2 };
190+
m_MessageManager.ClientConnected(1);
191+
m_MessageManager.ClientConnected(2);
192+
m_MessageManager.SetVersion(1, XXHash.Hash32(typeof(TestMessage).FullName), 0);
193+
m_MessageManager.SetVersion(2, XXHash.Hash32(typeof(TestMessage).FullName), 0);
194+
195+
for (var i = 0; i < clients.Length; ++i)
196+
{
197+
m_MessageManager.PeerMTUSizes[clients[i]] = maxMessageSize * (i + 1);
198+
}
199+
200+
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
201+
for (var i = 0; i < clients.Length; ++i)
202+
{
203+
for (var j = 0; j < ((m_MessageManager.PeerMTUSizes[clients[i]] - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size) + 1; ++j)
204+
{
205+
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, clients[i]);
206+
}
207+
}
208+
209+
m_MessageManager.ProcessSendQueues();
210+
Assert.AreEqual(2 * clients.Length, m_MessageSender.MessageQueue.Count);
211+
}
212+
213+
[Test]
214+
public void WhenExceedingPerClientBatchSizeGreaterThanDefault_OnlyOneNewBatcheIsCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
215+
{
216+
var message = GetMessage();
217+
m_MessageManager.NonFragmentedMessageMaxSize = 128;
218+
var clients = new ulong[] { 0, 1, 2 };
219+
m_MessageManager.ClientConnected(1);
220+
m_MessageManager.ClientConnected(2);
221+
m_MessageManager.SetVersion(1, XXHash.Hash32(typeof(TestMessage).FullName), 0);
222+
m_MessageManager.SetVersion(2, XXHash.Hash32(typeof(TestMessage).FullName), 0);
223+
224+
for (var i = 0; i < clients.Length; ++i)
225+
{
226+
m_MessageManager.PeerMTUSizes[clients[i]] = maxMessageSize * (i + 1);
227+
}
228+
229+
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
230+
for (var i = 0; i < clients.Length; ++i)
231+
{
232+
for (var j = 0; j < ((m_MessageManager.PeerMTUSizes[clients[i]] - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size) + 1; ++j)
233+
{
234+
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, clients[i]);
235+
}
236+
}
237+
238+
m_MessageManager.ProcessSendQueues();
239+
Assert.AreEqual(2 * clients.Length, m_MessageSender.MessageQueue.Count);
240+
}
241+
184242
[Test]
185243
public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
186244
{

0 commit comments

Comments
 (0)