Skip to content

Commit b097d74

Browse files
authored
fix: Fixed instability issues in the tests and optimized them so they would not take as long to run. (#2860)
1 parent 0c70ae3 commit b097d74

File tree

11 files changed

+1014
-609
lines changed

11 files changed

+1014
-609
lines changed

com.unity.netcode.gameobjects/TestHelpers/Runtime/MockTransport.cs

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using Unity.Collections;
34
using Random = UnityEngine.Random;
45

56
namespace Unity.Netcode.TestHelpers.Runtime
@@ -9,12 +10,14 @@ internal class MockTransport : NetworkTransport
910
private struct MessageData
1011
{
1112
public ulong FromClientId;
12-
public ArraySegment<byte> Payload;
13+
public FastBufferReader Payload;
1314
public NetworkEvent Event;
1415
public float AvailableTime;
16+
public int Sequence;
17+
public NetworkDelivery Delivery;
1518
}
1619

17-
private static Dictionary<ulong, Queue<MessageData>> s_MessageQueue = new Dictionary<ulong, Queue<MessageData>>();
20+
private static Dictionary<ulong, List<MessageData>> s_MessageQueue = new Dictionary<ulong, List<MessageData>>();
1821

1922
public override ulong ServerClientId { get; } = 0;
2023

@@ -25,39 +28,89 @@ private struct MessageData
2528
public float PacketDropRate;
2629
public float LatencyJitter;
2730

31+
public Dictionary<ulong, int> LastSentSequence = new Dictionary<ulong, int>();
32+
public Dictionary<ulong, int> LastReceivedSequence = new Dictionary<ulong, int>();
33+
2834
public NetworkManager NetworkManager;
2935

3036
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
3137
{
32-
if (Random.Range(0, 1) < PacketDropRate)
38+
if ((networkDelivery == NetworkDelivery.Unreliable || networkDelivery == NetworkDelivery.UnreliableSequenced) && Random.Range(0, 1) < PacketDropRate)
3339
{
3440
return;
3541
}
36-
var copy = new byte[payload.Array.Length];
37-
Array.Copy(payload.Array, copy, payload.Array.Length);
38-
s_MessageQueue[clientId].Enqueue(new MessageData { FromClientId = TransportId, Payload = new ArraySegment<byte>(copy, payload.Offset, payload.Count), Event = NetworkEvent.Data, AvailableTime = NetworkManager.RealTimeProvider.UnscaledTime + SimulatedLatencySeconds + Random.Range(-LatencyJitter, LatencyJitter) });
42+
43+
if (!LastSentSequence.ContainsKey(clientId))
44+
{
45+
LastSentSequence[clientId] = 1;
46+
}
47+
48+
var reader = new FastBufferReader(payload, Allocator.TempJob);
49+
s_MessageQueue[clientId].Add(new MessageData
50+
{
51+
FromClientId = TransportId,
52+
Payload = reader,
53+
Event = NetworkEvent.Data,
54+
AvailableTime = NetworkManager.RealTimeProvider.UnscaledTime + SimulatedLatencySeconds + Random.Range(-LatencyJitter, LatencyJitter),
55+
Sequence = ++LastSentSequence[clientId],
56+
Delivery = networkDelivery
57+
});
58+
s_MessageQueue[clientId].Sort(((a, b) => a.AvailableTime.CompareTo(b.AvailableTime)));
3959
}
4060

4161
public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
4262
{
4363
if (s_MessageQueue[TransportId].Count > 0)
4464
{
45-
var data = s_MessageQueue[TransportId].Peek();
46-
if (data.AvailableTime > NetworkManager.RealTimeProvider.UnscaledTime)
65+
MessageData data;
66+
for (; ; )
67+
{
68+
data = s_MessageQueue[TransportId][0];
69+
if (data.AvailableTime > NetworkManager.RealTimeProvider.UnscaledTime)
70+
{
71+
clientId = 0;
72+
payload = new ArraySegment<byte>();
73+
receiveTime = 0;
74+
return NetworkEvent.Nothing;
75+
}
76+
77+
s_MessageQueue[TransportId].RemoveAt(0);
78+
clientId = data.FromClientId;
79+
if (data.Event == NetworkEvent.Data && data.Delivery == NetworkDelivery.UnreliableSequenced && LastReceivedSequence.ContainsKey(clientId) && data.Sequence <= LastReceivedSequence[clientId])
80+
{
81+
continue;
82+
}
83+
84+
break;
85+
}
86+
87+
if (data.Delivery == NetworkDelivery.UnreliableSequenced)
88+
{
89+
LastReceivedSequence[clientId] = data.Sequence;
90+
}
91+
92+
payload = new ArraySegment<byte>();
93+
if (data.Event == NetworkEvent.Data)
4794
{
48-
clientId = 0;
49-
payload = new ArraySegment<byte>();
50-
receiveTime = 0;
51-
return NetworkEvent.Nothing;
95+
payload = data.Payload.ToArray();
96+
data.Payload.Dispose();
5297
}
5398

54-
s_MessageQueue[TransportId].Dequeue();
55-
clientId = data.FromClientId;
56-
payload = data.Payload;
5799
receiveTime = NetworkManager.RealTimeProvider.RealTimeSinceStartup;
58100
if (NetworkManager.IsServer && data.Event == NetworkEvent.Connect)
59101
{
60-
s_MessageQueue[data.FromClientId].Enqueue(new MessageData { Event = NetworkEvent.Connect, FromClientId = ServerClientId, Payload = new ArraySegment<byte>() });
102+
if (!LastSentSequence.ContainsKey(data.FromClientId))
103+
{
104+
LastSentSequence[data.FromClientId] = 1;
105+
}
106+
s_MessageQueue[data.FromClientId].Add(
107+
new MessageData
108+
{
109+
Event = NetworkEvent.Connect,
110+
FromClientId = ServerClientId,
111+
AvailableTime = NetworkManager.RealTimeProvider.UnscaledTime + SimulatedLatencySeconds + Random.Range(-LatencyJitter, LatencyJitter),
112+
Sequence = ++LastSentSequence[data.FromClientId]
113+
});
61114
}
62115
return data.Event;
63116
}
@@ -70,30 +123,45 @@ public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte
70123
public override bool StartClient()
71124
{
72125
TransportId = ++HighTransportId;
73-
s_MessageQueue[TransportId] = new Queue<MessageData>();
74-
s_MessageQueue[ServerClientId].Enqueue(new MessageData { Event = NetworkEvent.Connect, FromClientId = TransportId, Payload = new ArraySegment<byte>() });
126+
s_MessageQueue[TransportId] = new List<MessageData>();
127+
s_MessageQueue[ServerClientId].Add(
128+
new MessageData
129+
{
130+
Event = NetworkEvent.Connect,
131+
FromClientId = TransportId,
132+
});
75133
return true;
76134
}
77135

78136
public override bool StartServer()
79137
{
80-
s_MessageQueue[ServerClientId] = new Queue<MessageData>();
138+
s_MessageQueue[ServerClientId] = new List<MessageData>();
81139
return true;
82140
}
83141

84142
public override void DisconnectRemoteClient(ulong clientId)
85143
{
86-
s_MessageQueue[clientId].Enqueue(new MessageData { Event = NetworkEvent.Disconnect, FromClientId = TransportId, Payload = new ArraySegment<byte>() });
144+
s_MessageQueue[clientId].Add(
145+
new MessageData
146+
{
147+
Event = NetworkEvent.Disconnect,
148+
FromClientId = TransportId,
149+
});
87150
}
88151

89152
public override void DisconnectLocalClient()
90153
{
91-
s_MessageQueue[ServerClientId].Enqueue(new MessageData { Event = NetworkEvent.Disconnect, FromClientId = TransportId, Payload = new ArraySegment<byte>() });
154+
s_MessageQueue[ServerClientId].Add(
155+
new MessageData
156+
{
157+
Event = NetworkEvent.Disconnect,
158+
FromClientId = TransportId,
159+
});
92160
}
93161

94162
public override ulong GetCurrentRtt(ulong clientId)
95163
{
96-
return 0;
164+
return (ulong)(SimulatedLatencySeconds * 1000);
97165
}
98166

99167
public override void Shutdown()
@@ -105,14 +173,30 @@ public override void Initialize(NetworkManager networkManager = null)
105173
NetworkManager = networkManager;
106174
}
107175

176+
protected static void DisposeQueueItems()
177+
{
178+
foreach (var kvp in s_MessageQueue)
179+
{
180+
foreach (var value in kvp.Value)
181+
{
182+
if (value.Event == NetworkEvent.Data)
183+
{
184+
value.Payload.Dispose();
185+
}
186+
}
187+
}
188+
}
189+
108190
public static void Reset()
109191
{
192+
DisposeQueueItems();
110193
s_MessageQueue.Clear();
111194
HighTransportId = 0;
112195
}
113196

114197
public static void ClearQueues()
115198
{
199+
DisposeQueueItems();
116200
foreach (var kvp in s_MessageQueue)
117201
{
118202
kvp.Value.Clear();

com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,7 @@ protected void CreateAndStartNewClientWithTimeTravel()
542542
protected IEnumerator StopOneClient(NetworkManager networkManager, bool destroy = false)
543543
{
544544
NetcodeIntegrationTestHelpers.StopOneClient(networkManager, destroy);
545-
if (destroy)
546-
{
547-
AddRemoveNetworkManager(networkManager, false);
548-
}
545+
AddRemoveNetworkManager(networkManager, false);
549546
yield return WaitForConditionOrTimeOut(() => !networkManager.IsConnectedClient);
550547
}
551548

@@ -1043,6 +1040,17 @@ public IEnumerator TearDown()
10431040
VerboseDebug($"Exiting {nameof(TearDown)}");
10441041
LogWaitForMessages();
10451042
NetcodeLogAssert.Dispose();
1043+
if (m_EnableTimeTravel)
1044+
{
1045+
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests)
1046+
{
1047+
MockTransport.ClearQueues();
1048+
}
1049+
else
1050+
{
1051+
MockTransport.Reset();
1052+
}
1053+
}
10461054
}
10471055

10481056
/// <summary>
@@ -1592,8 +1600,17 @@ protected IEnumerator WaitForTicks(NetworkManager networkManager, int count)
15921600
/// </summary>
15931601
/// <param name="amountOfTimeInSeconds"></param>
15941602
/// <param name="numFramesToSimulate"></param>
1595-
protected static void TimeTravel(double amountOfTimeInSeconds, int numFramesToSimulate)
1603+
protected static void TimeTravel(double amountOfTimeInSeconds, int numFramesToSimulate = -1)
15961604
{
1605+
if (numFramesToSimulate < 0)
1606+
{
1607+
var frameRate = Application.targetFrameRate;
1608+
if (frameRate <= 0)
1609+
{
1610+
frameRate = 60;
1611+
}
1612+
numFramesToSimulate = Math.Max((int)(amountOfTimeInSeconds / frameRate), 1);
1613+
}
15971614
var interval = amountOfTimeInSeconds / numFramesToSimulate;
15981615
for (var i = 0; i < numFramesToSimulate; ++i)
15991616
{
@@ -1651,6 +1668,16 @@ public static void TimeTravelToNextTick()
16511668
TimeTravel(timePassed, frames);
16521669
}
16531670

1671+
private struct UpdateData
1672+
{
1673+
public MethodInfo Update;
1674+
public MethodInfo FixedUpdate;
1675+
public MethodInfo LateUpdate;
1676+
}
1677+
1678+
private static object[] s_EmptyObjectArray = { };
1679+
private static Dictionary<Type, UpdateData> s_UpdateFunctionCache = new Dictionary<Type, UpdateData>();
1680+
16541681
/// <summary>
16551682
/// Simulates one SDK frame. This can be used even without TimeTravel, though it's of somewhat less use
16561683
/// without TimeTravel, as, without the mock transport, it will likely not provide enough time for any
@@ -1673,34 +1700,34 @@ public static void SimulateOneFrame()
16731700
stage = NetworkUpdateStage.PostScriptLateUpdate;
16741701
}
16751702
NetworkUpdateLoop.RunNetworkUpdateStage(stage);
1676-
string methodName = string.Empty;
1677-
switch (stage)
1678-
{
1679-
case NetworkUpdateStage.FixedUpdate:
1680-
methodName = "FixedUpdate"; // mapping NetworkUpdateStage.FixedUpdate to MonoBehaviour.FixedUpdate
1681-
break;
1682-
case NetworkUpdateStage.Update:
1683-
methodName = "Update"; // mapping NetworkUpdateStage.Update to MonoBehaviour.Update
1684-
break;
1685-
case NetworkUpdateStage.PreLateUpdate:
1686-
methodName = "LateUpdate"; // mapping NetworkUpdateStage.PreLateUpdate to MonoBehaviour.LateUpdate
1687-
break;
1688-
}
16891703

1690-
if (!string.IsNullOrEmpty(methodName))
1704+
if (stage == NetworkUpdateStage.Update || stage == NetworkUpdateStage.FixedUpdate || stage == NetworkUpdateStage.PreLateUpdate)
16911705
{
1692-
#if UNITY_2023_1_OR_NEWER
1693-
foreach (var obj in Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID))
1694-
#else
1695-
foreach (var obj in Object.FindObjectsOfType<NetworkObject>())
1696-
#endif
1706+
foreach (var behaviour in Object.FindObjectsByType<NetworkBehaviour>(FindObjectsSortMode.None))
16971707
{
1698-
var method = obj.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
1699-
method?.Invoke(obj, new object[] { });
1700-
foreach (var behaviour in obj.ChildNetworkBehaviours)
1708+
var type = behaviour.GetType();
1709+
if (!s_UpdateFunctionCache.TryGetValue(type, out var updateData))
1710+
{
1711+
updateData = new UpdateData
1712+
{
1713+
Update = type.GetMethod("Update", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
1714+
FixedUpdate = type.GetMethod("FixedUpdate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
1715+
LateUpdate = type.GetMethod("LateUpdate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
1716+
};
1717+
s_UpdateFunctionCache[type] = updateData;
1718+
}
1719+
1720+
switch (stage)
17011721
{
1702-
var behaviourMethod = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
1703-
behaviourMethod?.Invoke(behaviour, new object[] { });
1722+
case NetworkUpdateStage.FixedUpdate:
1723+
updateData.FixedUpdate?.Invoke(behaviour, new object[] { });
1724+
break;
1725+
case NetworkUpdateStage.Update:
1726+
updateData.Update?.Invoke(behaviour, new object[] { });
1727+
break;
1728+
case NetworkUpdateStage.PreLateUpdate:
1729+
updateData.LateUpdate?.Invoke(behaviour, new object[] { });
1730+
break;
17041731
}
17051732
}
17061733
}

com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client
146146

147147
m_TransportClientId = m_ServerNetworkManager.ConnectionManager.ClientIdToTransportId(m_ClientId);
148148

149+
var clientManager = m_ClientNetworkManagers[0];
150+
149151
if (clientDisconnectType == ClientDisconnectType.ServerDisconnectsClient)
150152
{
151153
m_ClientNetworkManagers[0].OnClientDisconnectCallback += OnClientDisconnectCallback;
@@ -169,17 +171,17 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client
169171
{
170172
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!");
171173
Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!");
172-
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ClientNetworkManagers[0]), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!");
173-
Assert.IsTrue(m_DisconnectedEvent[m_ClientNetworkManagers[0]].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!");
174+
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(clientManager), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!");
175+
Assert.IsTrue(m_DisconnectedEvent[clientManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!");
174176
// Unregister for this event otherwise it will be invoked during teardown
175177
m_ServerNetworkManager.OnConnectionEvent -= OnConnectionEvent;
176178
}
177179
else
178180
{
179181
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!");
180182
Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!");
181-
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ClientNetworkManagers[0]), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!");
182-
Assert.IsTrue(m_DisconnectedEvent[m_ClientNetworkManagers[0]].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!");
183+
Assert.IsTrue(m_DisconnectedEvent.ContainsKey(clientManager), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!");
184+
Assert.IsTrue(m_DisconnectedEvent[clientManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!");
183185
}
184186

185187
if (m_OwnerPersistence == OwnerPersistence.DestroyWithOwner)

0 commit comments

Comments
 (0)