Skip to content

Commit 75c646a

Browse files
fix: LoadEventCompleted scene event not invoked when a client disconnects before finishing loading a scene [MTT-3396] (#1973)
* test This allows me to test this bug to verify it is no longer happening. * fix This fixes the issue where if a client disconnects during a scene event it would not get completed. This will also fix an exception on the client side where if the client was disconnected during a scene event while in the middle of loading or unloading a scene it will not try to continue processing the event. * test This includes the integration test that validates the fix. This also includes some minor adjustments to the integration test scene handler to account for handling a client disconnecting in the middle of a scene loading event. * Update CHANGELOG.md * update MTT-3396 Adding additional code that tracks the initial clients that were connected when the scene event starts in order to provide an accurate list of clients that timed out during scene loading. Cleaned up some of the code in the integration test as well as added additional comments. * Update CHANGELOG.md * Update com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs Co-authored-by: LPLafontaineB <[email protected]> * fix Fixing an issue where if a virtual mac machine was running slower than expected it would time out on certain deferred message tests. The primary area impacted was WaitForMessageOfTypeReceived which seemed to not use the DefaultTimeout value but 0.5s as the maximum time it would wait for the message. This just changes the timeout to the Default timeout for WaitForMessageOfTypeReceived and I noticed WaitForMessageOfTypeHandled used 0.5s for the default time out period and set that to use the DefaultTimeout. * fix increasing the timeout period. Co-authored-by: LPLafontaineB <[email protected]>
1 parent 8a109fa commit 75c646a

File tree

11 files changed

+396
-101
lines changed

11 files changed

+396
-101
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ Additional documentation and release notes are available at [Multiplayer Documen
2020
### Removed
2121

2222
### Fixed
23-
23+
- Fixed issue where one or more clients disconnecting during a scene event would cause `LoadEventCompleted` or `UnloadEventCompleted` to wait until the `NetworkConfig.LoadSceneTimeOut` period before being triggered. (#1973)
2424
- Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972)
25-
- Fixed endless dialog boxes when adding a NetworkBehaviour to a NetworkManager or vice-versa (#1947)
2625
- `FixedString` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper (#1961)
26+
- Fixed endless dialog boxes when adding a `NetworkBehaviour` to a `NetworkManager` or vice-versa. (#1947)
2727

2828
## [1.0.0-pre.9] - 2022-05-10
2929

com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress
891891
sceneEventData.SceneEventType = sceneEventProgress.SceneEventType;
892892
sceneEventData.ClientsCompleted = sceneEventProgress.DoneClients;
893893
sceneEventData.LoadSceneMode = sceneEventProgress.LoadSceneMode;
894-
sceneEventData.ClientsTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList();
894+
sceneEventData.ClientsTimedOut = sceneEventProgress.ClientsThatStartedSceneEvent.Except(sceneEventProgress.DoneClients).ToList();
895895

896896
var message = new SceneEventMessage
897897
{
@@ -1052,6 +1052,12 @@ private void OnClientUnloadScene(uint sceneEventId)
10521052
/// </summary>
10531053
private void OnSceneUnloaded(uint sceneEventId)
10541054
{
1055+
// If we are shutdown or about to shutdown, then ignore this event
1056+
if (!m_NetworkManager.IsListening || m_NetworkManager.ShutdownInProgress)
1057+
{
1058+
return;
1059+
}
1060+
10551061
var sceneEventData = SceneEventDataStore[sceneEventId];
10561062
// First thing we do, if we are a server, is to send the unload scene event.
10571063
if (m_NetworkManager.IsServer)
@@ -1257,13 +1263,18 @@ private void OnClientSceneLoadingEvent(uint sceneEventId)
12571263
OnLoad?.Invoke(m_NetworkManager.LocalClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
12581264
}
12591265

1260-
12611266
/// <summary>
12621267
/// Client and Server:
12631268
/// Generic on scene loaded callback method to be called upon a scene loading
12641269
/// </summary>
12651270
private void OnSceneLoaded(uint sceneEventId)
12661271
{
1272+
// If we are shutdown or about to shutdown, then ignore this event
1273+
if (!m_NetworkManager.IsListening || m_NetworkManager.ShutdownInProgress)
1274+
{
1275+
return;
1276+
}
1277+
12671278
var sceneEventData = SceneEventDataStore[sceneEventId];
12681279
var nextScene = GetAndAddNewlyLoadedSceneByName(SceneNameFromHash(sceneEventData.SceneHash));
12691280
if (!nextScene.isLoaded || !nextScene.IsValid())

com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventProgress.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ internal class SceneEventProgress
6161
internal List<ulong> DoneClients { get; } = new List<ulong>();
6262

6363
/// <summary>
64-
/// The NetworkTime at the moment the scene switch was initiated by the server.
64+
/// The local time when the scene event was "roughly started"
6565
/// </summary>
66-
internal NetworkTime TimeAtInitiation { get; }
66+
internal float TimeAtInitiation { get; }
6767

6868
/// <summary>
6969
/// Delegate type for when the switch scene progress is completed. Either by all clients done loading the scene or by time out.
@@ -105,22 +105,40 @@ internal class SceneEventProgress
105105

106106
internal LoadSceneMode LoadSceneMode;
107107

108+
internal List<ulong> ClientsThatStartedSceneEvent;
109+
108110
internal SceneEventProgress(NetworkManager networkManager, SceneEventProgressStatus status = SceneEventProgressStatus.Started)
109111
{
110112
if (status == SceneEventProgressStatus.Started)
111113
{
114+
// Track the clients that were connected when we started this event
115+
ClientsThatStartedSceneEvent = new List<ulong>(networkManager.ConnectedClientsIds);
112116
m_NetworkManager = networkManager;
113117
m_TimeOutCoroutine = m_NetworkManager.StartCoroutine(TimeOutSceneEventProgress());
114-
TimeAtInitiation = networkManager.LocalTime;
118+
TimeAtInitiation = Time.realtimeSinceStartup;
115119
}
116120
Status = status;
117121
}
118122

123+
/// <summary>
124+
/// Coroutine that checks to see if the scene event is complete every network tick period.
125+
/// This will handle completing the scene event when one or more client(s) disconnect(s)
126+
/// during a scene event and if it does not complete within the scene loading time out period
127+
/// it will time out the scene event.
128+
/// </summary>
119129
internal IEnumerator TimeOutSceneEventProgress()
120130
{
121-
yield return new WaitForSecondsRealtime(m_NetworkManager.NetworkConfig.LoadSceneTimeOut);
122-
TimedOut = true;
123-
CheckCompletion();
131+
var waitForNetworkTick = new WaitForSeconds(1.0f / m_NetworkManager.NetworkConfig.TickRate);
132+
while (!TimedOut && !IsCompleted)
133+
{
134+
yield return waitForNetworkTick;
135+
136+
CheckCompletion();
137+
if (!IsCompleted)
138+
{
139+
TimedOut = TimeAtInitiation - Time.realtimeSinceStartup >= m_NetworkManager.NetworkConfig.LoadSceneTimeOut;
140+
}
141+
}
124142
}
125143

126144
internal void AddClientAsDone(ulong clientId)

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public enum JobTypes
5454

5555
internal NetworkManager NetworkManager;
5656

57+
internal string NetworkManagerName;
58+
5759
/// <summary>
5860
/// Used to control when clients should attempt to fake-load a scene
5961
/// Note: Unit/Integration tests that only use <see cref="NetcodeIntegrationTestHelpers"/>
@@ -211,7 +213,7 @@ static internal IEnumerator JobQueueProcessor()
211213
while (QueuedSceneJobs.Count != 0)
212214
{
213215
CurrentQueuedSceneJob = QueuedSceneJobs.Dequeue();
214-
VerboseDebug($"[ITSH-START] {CurrentQueuedSceneJob.IntegrationTestSceneHandler.NetworkManager.name} processing {CurrentQueuedSceneJob.JobType} for scene {CurrentQueuedSceneJob.SceneName}.");
216+
VerboseDebug($"[ITSH-START] {CurrentQueuedSceneJob.IntegrationTestSceneHandler.NetworkManagerName} processing {CurrentQueuedSceneJob.JobType} for scene {CurrentQueuedSceneJob.SceneName}.");
215217
if (CurrentQueuedSceneJob.JobType == QueuedSceneJob.JobTypes.Loading)
216218
{
217219
yield return ProcessLoadingSceneJob(CurrentQueuedSceneJob);
@@ -220,7 +222,7 @@ static internal IEnumerator JobQueueProcessor()
220222
{
221223
yield return ProcessUnloadingSceneJob(CurrentQueuedSceneJob);
222224
}
223-
VerboseDebug($"[ITSH-STOP] {CurrentQueuedSceneJob.IntegrationTestSceneHandler.NetworkManager.name} processing {CurrentQueuedSceneJob.JobType} for scene {CurrentQueuedSceneJob.SceneName}.");
225+
VerboseDebug($"[ITSH-STOP] {CurrentQueuedSceneJob.IntegrationTestSceneHandler.NetworkManagerName} processing {CurrentQueuedSceneJob.JobType} for scene {CurrentQueuedSceneJob.SceneName}.");
224226
}
225227
SceneJobProcessor = null;
226228
yield break;
@@ -357,6 +359,7 @@ public IntegrationTestSceneHandler(NetworkManager networkManager)
357359
{
358360
networkManager.SceneManager.OverrideGetAndAddNewlyLoadedSceneByName = GetAndAddNewlyLoadedSceneByName;
359361
NetworkManagers.Add(networkManager);
362+
NetworkManagerName = networkManager.name;
360363
if (s_WaitForSeconds == null)
361364
{
362365
s_WaitForSeconds = new WaitForSeconds(1.0f / networkManager.NetworkConfig.TickRate);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
1616
public static class NetcodeIntegrationTestHelpers
1717
{
1818
public const int DefaultMinFrames = 1;
19-
public const float DefaultTimeout = 2f;
19+
public const float DefaultTimeout = 4f;
2020
private static List<NetworkManager> s_NetworkManagerInstances = new List<NetworkManager>();
2121
private static Dictionary<NetworkManager, MultiInstanceHooks> s_Hooks = new Dictionary<NetworkManager, MultiInstanceHooks>();
2222
private static bool s_IsStarted;
@@ -733,7 +733,7 @@ public static IEnumerator WaitForCondition(Func<bool> predicate, ResultWrapper<b
733733
/// </summary>
734734
/// <param name="result">The result. If null, it will fail if the predicate is not met</param>
735735
/// <param name="timeout">The max time in seconds to wait for</param>
736-
internal static IEnumerator WaitForMessageOfTypeReceived<T>(NetworkManager toBeReceivedBy, ResultWrapper<bool> result = null, float timeout = 0.5f) where T : INetworkMessage
736+
internal static IEnumerator WaitForMessageOfTypeReceived<T>(NetworkManager toBeReceivedBy, ResultWrapper<bool> result = null, float timeout = DefaultTimeout) where T : INetworkMessage
737737
{
738738
var hooks = s_Hooks[toBeReceivedBy];
739739
var check = new MessageReceiveCheckWithResult { CheckType = typeof(T) };
@@ -761,7 +761,7 @@ internal static IEnumerator WaitForMessageOfTypeReceived<T>(NetworkManager toBeR
761761
/// </summary>
762762
/// <param name="result">The result. If null, it will fail if the predicate is not met</param>
763763
/// <param name="timeout">The max time in seconds to wait for</param>
764-
internal static IEnumerator WaitForMessageOfTypeHandled<T>(NetworkManager toBeReceivedBy, ResultWrapper<bool> result = null, float timeout = 0.5f) where T : INetworkMessage
764+
internal static IEnumerator WaitForMessageOfTypeHandled<T>(NetworkManager toBeReceivedBy, ResultWrapper<bool> result = null, float timeout = DefaultTimeout) where T : INetworkMessage
765765
{
766766
var hooks = s_Hooks[toBeReceivedBy];
767767
if (!hooks.HandleChecks.ContainsKey(typeof(T)))

0 commit comments

Comments
 (0)