Skip to content

feat: Improve access to simulator parameters #1745

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 6 commits into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions com.unity.netcode.adapter.utp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to this package will be documented in this file. The format

### Added

- New parameters are available to simulate network conditions (delay, jitter, packet loss) in the editor and in development builds. The parameters are available under the 'Debug Simulator' section of the 'Unity Transport' component, or can be set with the `SetDebugSimulatorParameters` call. (#1745)

### Changed

- Updated Unity Transport package to 1.0.0-pre.13. (#1696)
Expand Down
135 changes: 93 additions & 42 deletions com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,53 @@ public static implicit operator ConnectionAddressData(NetworkEndPoint d) =>

public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData;

[Serializable]
public struct SimulatorParameters
{
[Tooltip("Delay to add to every send and received packet (in milliseconds). Only applies in the editor " +
"and in development builds. The value is ignored in production builds.")]
[SerializeField] public int PacketDelayMS;

[Tooltip("Jitter (random variation) to add/substract to the packet delay (in milliseconds). Only " +
"applies in the editor and in development builds. The value is ignored in production builds.")]
[SerializeField] public int PacketJitterMS;

[Tooltip("Percentage of sent and received packets to drop. Only applies in the editor and in the editor " +
"and in developments builds.")]
[SerializeField] public int PacketDropRate;
}

public SimulatorParameters DebugSimulator = new SimulatorParameters
{
PacketDelayMS = 0,
PacketJitterMS = 0,
PacketDropRate = 0
};

// Only for backward compatibility with how we used to handle simulator parameters.
#if DEVELOPMENT_BUILD
[Obsolete("Use SetDebugSimulatorParameters() instead.")]
public int ClientPacketDelayMs
{
get => DebugSimulator.PacketDelayMS;
set => DebugSimulator.PacketDelayMS = value;
}

[Obsolete("Use SetDebugSimulatorParameters() instead.")]
public int ClientPacketJitterMs
{
get => DebugSimulator.PacketJitterMS;
set => DebugSimulator.PacketJitterMS = value;
}

[Obsolete("Use SetDebugSimulatorParameters() instead.")]
public int ClientPacketDropRate
{
get => DebugSimulator.PacketDropRate;
set => DebugSimulator.PacketDropRate = value;
}
#endif

private State m_State = State.Disconnected;
private NetworkDriver m_Driver;
private NetworkSettings m_NetworkSettings;
Expand All @@ -184,43 +231,6 @@ public static implicit operator ConnectionAddressData(NetworkEndPoint d) =>

internal NetworkManager NetworkManager;

#if UNITY_EDITOR
private static int ClientPacketDelayMs => UnityEditor.EditorPrefs.GetInt($"NetcodeGameObjects_{Application.productName}_ClientDelay");
private static int ClientPacketJitterMs => UnityEditor.EditorPrefs.GetInt($"NetcodeGameObjects_{Application.productName}_ClientJitter");
private static int ClientPacketDropRate => UnityEditor.EditorPrefs.GetInt($"NetcodeGameObjects_{Application.productName}_ClientDropRate");
#elif DEVELOPMENT_BUILD
public static int ClientPacketDelayMs = 0;
public static int ClientPacketJitterMs = 0;
public static int ClientPacketDropRate = 0;
#endif
#if UNITY_EDITOR || DEVELOPMENT_BUILD
public SimulatorUtility.Parameters ClientSimulatorParameters
{
get
{
var packetDelay = ClientPacketDelayMs;
var jitter = ClientPacketJitterMs;
if (jitter > packetDelay)
{
jitter = packetDelay;
}

var packetDrop = ClientPacketDropRate;
int networkRate = 60; // TODO: read from some better place
// All 3 packet types every frame stored for maximum delay, doubled for safety margin
int maxPackets = 2 * (networkRate * 3 * packetDelay + 999) / 1000;
return new SimulatorUtility.Parameters
{
MaxPacketSize = NetworkParameterConstants.MTU,
MaxPacketCount = maxPackets,
PacketDelayMs = packetDelay,
PacketJitterMs = jitter,
PacketDropPercentage = packetDrop
};
}
}
#endif

/// <summary>
/// SendQueue dictionary is used to batch events instead of sending them immediately.
/// </summary>
Expand Down Expand Up @@ -478,6 +488,26 @@ public void SetConnectionData(NetworkEndPoint endPoint, NetworkEndPoint listenEn
SetConnectionData(serverAddress, endPoint.Port, listenAddress);
}

/// <summary>Set the parameters for the debug simulator.</summary>
/// <param name="packetDelay">Packet delay in milliseconds.</param>
/// <param name="packetJitter">Packet jitter in milliseconds.</param>
/// <param name="dropRate">Packet drop percentage.</param>
public void SetDebugSimulatorParameters(int packetDelay, int packetJitter, int dropRate)
{
if (m_Driver.IsCreated)
{
Debug.LogError("SetDebugSimulatorParameters() must be called before StartClient() or StartServer().");
return;
}

DebugSimulator = new SimulatorParameters
{
PacketDelayMS = packetDelay,
PacketJitterMS = packetJitter,
PacketDropRate = dropRate
};
}

private bool StartRelayServer()
{
//This comparison is currently slow since RelayServerData does not implement a custom comparison operator that doesn't use
Expand Down Expand Up @@ -974,6 +1004,28 @@ public override void Shutdown()
m_ServerClientId = 0;
}

private void ConfigureSimulator()
{
#if UNITY_EDITOR
// Backward-compatibility with how we used to handle simulator parameters.
var packetDelay = UnityEditor.EditorPrefs.GetInt($"NetcodeGameObjects_{Application.productName}_ClientDelay", DebugSimulator.PacketDelayMS);
var packetJitter = UnityEditor.EditorPrefs.GetInt($"NetcodeGameObjects_{Application.productName}_ClientJitter", DebugSimulator.PacketJitterMS);
var dropRate = UnityEditor.EditorPrefs.GetInt($"NetcodeGameObjects_{Application.productName}_ClientDropRate", DebugSimulator.PacketDropRate);
#else
var packetDelay = DebugSimulator.PacketDelayMS;
var packetJitter = DebugSimulator.PacketJitterMS;
var dropRate = DebugSimulator.PacketDropRate;
#endif

m_NetworkSettings.WithSimulatorStageParameters(
maxPacketCount: 300, // TODO Is there any way to compute a better value?
maxPacketSize: NetworkParameterConstants.MTU,
packetDelayMs: packetDelay,
packetJitterMs: packetJitter,
packetDropPercentage: dropRate
);
}

public void CreateDriver(UnityTransport transport, out NetworkDriver driver,
out NetworkPipeline unreliableFragmentedPipeline,
out NetworkPipeline unreliableSequencedFragmentedPipeline,
Expand All @@ -986,11 +1038,9 @@ public void CreateDriver(UnityTransport transport, out NetworkDriver driver,

#if UNITY_EDITOR || DEVELOPMENT_BUILD
maxFrameTimeMS = 100;

var simulatorParams = ClientSimulatorParameters;

m_NetworkSettings.AddRawParameterStruct(ref simulatorParams);
ConfigureSimulator();
#endif

m_NetworkSettings.WithNetworkConfigParameters(
maxConnectAttempts: transport.m_MaxConnectAttempts,
connectTimeoutMS: transport.m_ConnectTimeoutMS,
Expand All @@ -999,8 +1049,9 @@ public void CreateDriver(UnityTransport transport, out NetworkDriver driver,
maxFrameTimeMS: maxFrameTimeMS);

driver = NetworkDriver.Create(m_NetworkSettings);

#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (simulatorParams.PacketDelayMs > 0 || simulatorParams.PacketDropInterval > 0)
if (DebugSimulator.PacketDelayMS > 0 || DebugSimulator.PacketDropRate > 0)
{
unreliableFragmentedPipeline = driver.CreatePipeline(
typeof(FragmentationPipelineStage),
Expand Down
27 changes: 27 additions & 0 deletions com.unity.netcode.adapter.utp/Tests/Runtime/TransportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,33 @@ public IEnumerator SendCompletesOnUnreliableSendQueueOverflow()

yield return null;
}

// Check that simulator parameters are effective. We only check with the drop rate, because
// that's easy to check and we only really want to make sure the simulator parameters are
// configured properly (the simulator pipeline stage is already well-tested in UTP).
[UnityTest]
[UnityPlatform(include = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor })]
public IEnumerator SimulatorParametersAreEffective()
{
InitializeTransport(out m_Server, out m_ServerEvents);
InitializeTransport(out m_Client1, out m_Client1Events);

m_Server.SetDebugSimulatorParameters(0, 0, 100);

m_Server.StartServer();
m_Client1.StartClient();

yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);

var data = new ArraySegment<byte>(new byte[] { 42 });
m_Client1.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);

yield return new WaitForSeconds(MaxNetworkEventWaitTime);

Assert.AreEqual(1, m_ServerEvents.Count);

yield return null;
}
}
}
#endif