Skip to content

feat: Enable fragmentation for unreliable delivery (UTP) #1512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 9, 2021
Merged
1 change: 1 addition & 0 deletions com.unity.netcode.adapter.utp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to this package will be documented in this file. The format

- Removed 'Maximum Packet Size' configuration field in the inspector. This would cause confusion since the maximum packet size is in effect always the MTU (1400 bytes on most platforms).
- Updated com.unity.transport to 1.0.0-pre.10
- All delivery methods now support fragmentation, meaning the 'Send Queue Batch Size' setting (which controls the maximum payload size) now applies to all delivery methods, not just reliable ones.

### Fixed

Expand Down
47 changes: 38 additions & 9 deletions com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ namespace Unity.Netcode
/// </summary>
public interface INetworkStreamDriverConstructor
{
void CreateDriver(UnityTransport transport, out NetworkDriver driver, out NetworkPipeline unreliableSequencedPipeline, out NetworkPipeline reliableSequencedFragmentedPipeline);
void CreateDriver(
UnityTransport transport,
out NetworkDriver driver,
out NetworkPipeline unreliableFragmentedPipeline,
out NetworkPipeline unreliableSequencedFragmentedPipeline,
out NetworkPipeline reliableSequencedFragmentedPipeline);
}

public static class ErrorUtilities
Expand Down Expand Up @@ -152,7 +157,8 @@ public static implicit operator ConnectionAddressData(NetworkEndPoint d) =>
private NetworkConnection m_ServerConnection;
private ulong m_ServerClientId;

private NetworkPipeline m_UnreliableSequencedPipeline;
private NetworkPipeline m_UnreliableFragmentedPipeline;
private NetworkPipeline m_UnreliableSequencedFragmentedPipeline;
private NetworkPipeline m_ReliableSequencedFragmentedPipeline;

public override ulong ServerClientId => m_ServerClientId;
Expand Down Expand Up @@ -205,7 +211,12 @@ public SimulatorUtility.Parameters ClientSimulatorParameters

private void InitDriver()
{
DriverConstructor.CreateDriver(this, out m_Driver, out m_UnreliableSequencedPipeline, out m_ReliableSequencedFragmentedPipeline);
DriverConstructor.CreateDriver(
this,
out m_Driver,
out m_UnreliableFragmentedPipeline,
out m_UnreliableSequencedFragmentedPipeline,
out m_ReliableSequencedFragmentedPipeline);
}

private void DisposeDriver()
Expand All @@ -221,10 +232,10 @@ private NetworkPipeline SelectSendPipeline(NetworkDelivery delivery)
switch (delivery)
{
case NetworkDelivery.Unreliable:
return NetworkPipeline.Null;
return m_UnreliableFragmentedPipeline;

case NetworkDelivery.UnreliableSequenced:
return m_UnreliableSequencedPipeline;
return m_UnreliableSequencedFragmentedPipeline;

case NetworkDelivery.Reliable:
case NetworkDelivery.ReliableSequenced:
Expand Down Expand Up @@ -729,7 +740,10 @@ public override void Shutdown()
m_ServerClientId = 0;
}

public void CreateDriver(UnityTransport transport, out NetworkDriver driver, out NetworkPipeline unreliableSequencedPipeline, out NetworkPipeline reliableSequencedFragmentedPipeline)
public void CreateDriver(UnityTransport transport, out NetworkDriver driver,
out NetworkPipeline unreliableFragmentedPipeline,
out NetworkPipeline unreliableSequencedFragmentedPipeline,
out NetworkPipeline reliableSequencedFragmentedPipeline)
{
#if MULTIPLAYER_TOOLS
NetworkPipelineStageCollection.RegisterPipelineStage(new NetworkMetricsPipelineStage());
Expand All @@ -755,7 +769,12 @@ public void CreateDriver(UnityTransport transport, out NetworkDriver driver, out
#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (simulatorParams.PacketDelayMs > 0 || simulatorParams.PacketDropInterval > 0)
{
unreliableSequencedPipeline = driver.CreatePipeline(
unreliableFragmentedPipeline = driver.CreatePipeline(
typeof(FragmentationPipelineStage),
typeof(SimulatorPipelineStage),
typeof(SimulatorPipelineStageInSend));
unreliableSequencedFragmentedPipeline = driver.CreatePipeline(
typeof(FragmentationPipelineStage),
typeof(UnreliableSequencedPipelineStage),
typeof(SimulatorPipelineStage),
typeof(SimulatorPipelineStageInSend)
Expand All @@ -776,13 +795,23 @@ public void CreateDriver(UnityTransport transport, out NetworkDriver driver, out
else
#endif
{
unreliableSequencedPipeline = driver.CreatePipeline(typeof(UnreliableSequencedPipelineStage)

unreliableFragmentedPipeline = driver.CreatePipeline(
typeof(FragmentationPipelineStage)
#if MULTIPLAYER_TOOLS
,typeof(NetworkMetricsPipelineStage)
#endif
);
unreliableSequencedFragmentedPipeline = driver.CreatePipeline(
typeof(FragmentationPipelineStage),
typeof(UnreliableSequencedPipelineStage)
#if MULTIPLAYER_TOOLS
,typeof(NetworkMetricsPipelineStage)
#endif
);
reliableSequencedFragmentedPipeline = driver.CreatePipeline(
typeof(FragmentationPipelineStage), typeof(ReliableSequencedPipelineStage)
typeof(FragmentationPipelineStage),
typeof(ReliableSequencedPipelineStage)
#if MULTIPLAYER_TOOLS
,typeof(NetworkMetricsPipelineStage)
#endif
Expand Down
53 changes: 29 additions & 24 deletions com.unity.netcode.adapter.utp/Tests/Runtime/TransportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ namespace Unity.Netcode.UTP.RuntimeTests
{
public class TransportTests
{
// No need to test all reliable delivery methods since they all map to the same pipeline.
private static readonly NetworkDelivery[] k_DeliveryParameters =
{
NetworkDelivery.Unreliable,
NetworkDelivery.UnreliableSequenced,
NetworkDelivery.Reliable
};

private UnityTransport m_Server, m_Client1, m_Client2;
private List<TransportEvent> m_ServerEvents, m_Client1Events, m_Client2Events;

Expand Down Expand Up @@ -55,7 +63,7 @@ public IEnumerator Cleanup()

// Check if can make a simple data exchange.
[UnityTest]
public IEnumerator PingPong()
public IEnumerator PingPong([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
Copy link
Contributor

@0xFA11 0xFA11 Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know about [ValueSource("k_DeliveryParameters")] syntax, nice!

{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -66,28 +74,25 @@ public IEnumerator PingPong()
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);

var ping = new ArraySegment<byte>(Encoding.ASCII.GetBytes("ping"));
m_Client1.Send(m_Client1.ServerClientId, ping, NetworkDelivery.ReliableSequenced);
m_Client1.Send(m_Client1.ServerClientId, ping, delivery);

yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);

Assert.That(m_ServerEvents[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("ping")));

var pong = new ArraySegment<byte>(Encoding.ASCII.GetBytes("pong"));
m_Server.Send(m_ServerEvents[0].ClientID, pong, NetworkDelivery.ReliableSequenced);
m_Server.Send(m_ServerEvents[0].ClientID, pong, delivery);

yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);

Assert.That(m_Client1Events[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("pong")));

// server.Shutdown();
// client.Shutdown();

yield return null;
}

// Check if can make a simple data exchange (both ways at a time).
[UnityTest]
public IEnumerator PingPongSimultaneous()
public IEnumerator PingPongSimultaneous([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -98,8 +103,8 @@ public IEnumerator PingPongSimultaneous()
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);

var ping = new ArraySegment<byte>(Encoding.ASCII.GetBytes("ping"));
m_Server.Send(m_ServerEvents[0].ClientID, ping, NetworkDelivery.ReliableSequenced);
m_Client1.Send(m_Client1.ServerClientId, ping, NetworkDelivery.ReliableSequenced);
m_Server.Send(m_ServerEvents[0].ClientID, ping, delivery);
m_Client1.Send(m_Client1.ServerClientId, ping, delivery);

// Once one event is in the other should be too.
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
Expand All @@ -108,8 +113,8 @@ public IEnumerator PingPongSimultaneous()
Assert.That(m_Client1Events[1].Data, Is.EquivalentTo(Encoding.ASCII.GetBytes("ping")));

var pong = new ArraySegment<byte>(Encoding.ASCII.GetBytes("pong"));
m_Server.Send(m_ServerEvents[0].ClientID, pong, NetworkDelivery.ReliableSequenced);
m_Client1.Send(m_Client1.ServerClientId, pong, NetworkDelivery.ReliableSequenced);
m_Server.Send(m_ServerEvents[0].ClientID, pong, delivery);
m_Client1.Send(m_Client1.ServerClientId, pong, delivery);

// Once one event is in the other should be too.
yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);
Expand All @@ -121,7 +126,7 @@ public IEnumerator PingPongSimultaneous()
}

[UnityTest]
public IEnumerator SendMaximumPayloadSize()
public IEnumerator SendMaximumPayloadSize([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -132,15 +137,15 @@ public IEnumerator SendMaximumPayloadSize()
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);

var payload = new ArraySegment<byte>(new byte[UnityTransport.InitialBatchQueueSize]);
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.ReliableFragmentedSequenced);
m_Client1.Send(m_Client1.ServerClientId, payload, delivery);

yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);

yield return null;
}

[UnityTest]
public IEnumerator FilledSendQueueMultipleSends()
public IEnumerator FilledSendQueueMultipleSends([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -158,7 +163,7 @@ public IEnumerator FilledSendQueueMultipleSends()
// Without that we wouldn't fill the send queue; it would get flushed right when we
// try to send the last message.
var payload = new ArraySegment<byte>(new byte[1024 - BatchedSendQueue.PerMessageOverhead]);
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.ReliableFragmentedSequenced);
m_Client1.Send(m_Client1.ServerClientId, payload, delivery);
}

// Manually wait. This ends up generating quite a bit of packets and it might take a
Expand All @@ -179,7 +184,7 @@ public IEnumerator FilledSendQueueMultipleSends()

// Check making multiple sends to a client in a single frame.
[UnityTest]
public IEnumerator MultipleSendsSingleFrame()
public IEnumerator MultipleSendsSingleFrame([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -190,10 +195,10 @@ public IEnumerator MultipleSendsSingleFrame()
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);

var data1 = new ArraySegment<byte>(new byte[] { 11 });
m_Client1.Send(m_Client1.ServerClientId, data1, NetworkDelivery.ReliableSequenced);
m_Client1.Send(m_Client1.ServerClientId, data1, delivery);

var data2 = new ArraySegment<byte>(new byte[] { 22 });
m_Client1.Send(m_Client1.ServerClientId, data2, NetworkDelivery.ReliableSequenced);
m_Client1.Send(m_Client1.ServerClientId, data2, delivery);

yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);

Expand All @@ -208,7 +213,7 @@ public IEnumerator MultipleSendsSingleFrame()

// Check sending data to multiple clients.
[UnityTest]
public IEnumerator SendMultipleClients()
public IEnumerator SendMultipleClients([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -228,10 +233,10 @@ public IEnumerator SendMultipleClients()
Assert.AreEqual(2, m_ServerEvents.Count);

var data1 = new ArraySegment<byte>(new byte[] { 11 });
m_Server.Send(m_ServerEvents[0].ClientID, data1, NetworkDelivery.ReliableSequenced);
m_Server.Send(m_ServerEvents[0].ClientID, data1, delivery);

var data2 = new ArraySegment<byte>(new byte[] { 22 });
m_Server.Send(m_ServerEvents[1].ClientID, data2, NetworkDelivery.ReliableSequenced);
m_Server.Send(m_ServerEvents[1].ClientID, data2, delivery);

// Once one has received its data, the other should have too.
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
Expand All @@ -249,7 +254,7 @@ public IEnumerator SendMultipleClients()

// Check receiving data from multiple clients.
[UnityTest]
public IEnumerator ReceiveMultipleClients()
public IEnumerator ReceiveMultipleClients([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);
Expand All @@ -266,10 +271,10 @@ public IEnumerator ReceiveMultipleClients()
}

var data1 = new ArraySegment<byte>(new byte[] { 11 });
m_Client1.Send(m_Client1.ServerClientId, data1, NetworkDelivery.ReliableSequenced);
m_Client1.Send(m_Client1.ServerClientId, data1, delivery);

var data2 = new ArraySegment<byte>(new byte[] { 22 });
m_Client2.Send(m_Client2.ServerClientId, data2, NetworkDelivery.ReliableSequenced);
m_Client2.Send(m_Client2.ServerClientId, data2, delivery);

yield return WaitForNetworkEvent(NetworkEvent.Data, m_ServerEvents);

Expand Down