Skip to content

Commit bd5c757

Browse files
test
Adding test that verifies a NetworkManager can be started when OnClientStopped is invoked and that OnServerStopped is not invoked if the NetworkManager is started again during an OnClientStopped invocation (i.e. host).
1 parent 85964c3 commit bd5c757

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using NUnit.Framework;
6+
using Unity.Netcode.TestHelpers.Runtime;
7+
using UnityEngine.TestTools;
8+
9+
namespace Unity.Netcode.RuntimeTests
10+
{
11+
[TestFixture(NetworkTopologyTypes.ClientServer)]
12+
internal class NetworkManagerStartStopTests : NetcodeIntegrationTest
13+
{
14+
private const int k_NumberOfSessions = 5;
15+
protected override int NumberOfClients => 2;
16+
private OnClientStoppedHandler m_StoppedHandler;
17+
private int m_ExpectedNumberOfClients = 0;
18+
public NetworkManagerStartStopTests(NetworkTopologyTypes networkTopologyType) : base(networkTopologyType, HostOrServer.Host) { }
19+
20+
/// <summary>
21+
/// This test will not work with the CMB service since it requires the service
22+
/// to remain active after all clients have disconnected.
23+
/// </summary>
24+
protected override bool UseCMBService()
25+
{
26+
return false;
27+
}
28+
29+
private void ShutdownIfListening()
30+
{
31+
var networkManager = m_StoppedHandler.NetworkManager;
32+
if (networkManager.IsListening)
33+
{
34+
m_StoppedHandler.NetworkManager.Shutdown();
35+
}
36+
}
37+
38+
private bool NetworkManagerCompletedSessionCount(StringBuilder errorLog)
39+
{
40+
// Once the session count is decremented to zero the condition has been met.
41+
if (m_StoppedHandler.SessionCount != 0)
42+
{
43+
// If we are a host, then only shutdown once all clients have reconnected
44+
if (m_StoppedHandler.IsSessionAuthority && m_StoppedHandler.NetworkManager.ConnectedClientsIds.Count != m_ExpectedNumberOfClients)
45+
{
46+
errorLog.Append($"[{m_StoppedHandler.NetworkManager.name}] Waiting for {m_ExpectedNumberOfClients} clients to connect but there are only {m_StoppedHandler.NetworkManager.ConnectedClientsIds.Count} connected!");
47+
return false;
48+
}
49+
ShutdownIfListening();
50+
errorLog.Append($"[{m_StoppedHandler.NetworkManager.name}] Still has a session count of {m_StoppedHandler.SessionCount}!");
51+
}
52+
return errorLog.Length == 0;
53+
}
54+
55+
[UnityTest]
56+
public IEnumerator StartFromWithinOnClientStopped()
57+
{
58+
var authority = GetAuthorityNetworkManager();
59+
m_ExpectedNumberOfClients = authority.ConnectedClientsIds.Count;
60+
61+
// Validate a client can disconnect and immediately reconnect from within OnClientStopped
62+
m_StoppedHandler = new OnClientStoppedHandler(k_NumberOfSessions, GetNonAuthorityNetworkManager());
63+
ShutdownIfListening();
64+
yield return WaitForConditionOrTimeOut(NetworkManagerCompletedSessionCount);
65+
AssertOnTimeout($"Not all {nameof(NetworkManager)} instances finished their sessions!");
66+
67+
// Validate a host can disconnect and immediately reconnect from within OnClientStopped
68+
m_StoppedHandler = new OnHostStoppedHandler(k_NumberOfSessions, authority, m_NetworkManagers.ToList());
69+
ShutdownIfListening();
70+
yield return WaitForConditionOrTimeOut(NetworkManagerCompletedSessionCount);
71+
AssertOnTimeout($"Not all {nameof(NetworkManager)} instances finished their sessions!");
72+
73+
// Verify OnServerStopped is not invoked if NetworkManager is started again within OnClientStopped (it should not invoke if it is listening).
74+
Assert.False((m_StoppedHandler as OnHostStoppedHandler).OnServerStoppedInvoked, $"{nameof(NetworkManager.OnServerStopped)} was invoked when it should not have been invoked!");
75+
}
76+
}
77+
78+
internal class OnHostStoppedHandler : OnClientStoppedHandler
79+
{
80+
public bool OnServerStoppedInvoked = false;
81+
82+
private List<NetworkManager> m_Clients = new List<NetworkManager>();
83+
84+
private Networking.Transport.NetworkEndpoint m_Endpoint;
85+
86+
protected override void OnClientStopped(bool wasHost)
87+
{
88+
m_Endpoint.Port++;
89+
var unityTransport = (Transports.UTP.UnityTransport)NetworkManager.NetworkConfig.NetworkTransport;
90+
unityTransport.SetConnectionData(m_Endpoint);
91+
// Make sure all clients are shutdown or shutting down
92+
foreach (var networkManager in m_Clients)
93+
{
94+
if (networkManager.IsListening && !networkManager.ShutdownInProgress)
95+
{
96+
networkManager.Shutdown();
97+
}
98+
}
99+
100+
base.OnClientStopped(wasHost);
101+
if (SessionCount != 0)
102+
{
103+
NetworkManager.StartCoroutine(StartClients());
104+
}
105+
106+
}
107+
108+
private IEnumerator StartClients()
109+
{
110+
var nextPhase = false;
111+
var timeout = UnityEngine.Time.realtimeSinceStartup + 5.0f;
112+
while (!nextPhase)
113+
{
114+
if (!nextPhase && timeout < UnityEngine.Time.realtimeSinceStartup)
115+
{
116+
Assert.Fail($"Timed out waiting for all {nameof(NetworkManager)} instances to shutdown!");
117+
yield break;
118+
}
119+
120+
nextPhase = true;
121+
foreach (var networkManager in m_Clients)
122+
{
123+
if (networkManager.ShutdownInProgress || networkManager.IsListening)
124+
{
125+
nextPhase = false;
126+
}
127+
}
128+
yield return null;
129+
}
130+
131+
// Now, start all of the clients and have them connect again
132+
foreach (var networkManager in m_Clients)
133+
{
134+
var unityTransport = (Transports.UTP.UnityTransport)networkManager.NetworkConfig.NetworkTransport;
135+
unityTransport.SetConnectionData(m_Endpoint);
136+
networkManager.StartClient();
137+
}
138+
}
139+
140+
public OnHostStoppedHandler(int numberOfSessions, NetworkManager authority, List<NetworkManager> networkManagers) : base(numberOfSessions, authority)
141+
{
142+
m_Endpoint = ((Transports.UTP.UnityTransport)authority.NetworkConfig.NetworkTransport).GetLocalEndpoint();
143+
networkManagers.Remove(authority);
144+
m_Clients = networkManagers;
145+
authority.OnServerStopped += OnServerStopped;
146+
}
147+
148+
private void OnServerStopped(bool wasHost)
149+
{
150+
OnServerStoppedInvoked = SessionCount != 0;
151+
}
152+
}
153+
154+
internal class OnClientStoppedHandler
155+
{
156+
public NetworkManager NetworkManager { get; private set; }
157+
158+
public int SessionCount { get; private set; }
159+
public bool IsSessionAuthority { get; private set; }
160+
161+
protected virtual void OnClientStopped(bool wasHost)
162+
{
163+
SessionCount--;
164+
if (SessionCount <= 0)
165+
{
166+
NetworkManager.OnClientStopped -= OnClientStopped;
167+
return;
168+
}
169+
170+
if (wasHost)
171+
{
172+
NetworkManager.StartHost();
173+
}
174+
else
175+
{
176+
NetworkManager.StartClient();
177+
}
178+
}
179+
180+
public OnClientStoppedHandler(int sessionCount, NetworkManager networkManager)
181+
{
182+
NetworkManager = networkManager;
183+
NetworkManager.OnClientStopped += OnClientStopped;
184+
SessionCount = sessionCount;
185+
IsSessionAuthority = networkManager.IsServer || networkManager.LocalClient.IsSessionOwner;
186+
}
187+
188+
public OnClientStoppedHandler() { }
189+
190+
}
191+
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerStartStopTests.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)