Skip to content

Commit 5da0247

Browse files
fix: SceneEventProgress OnComplete not invoked on timeouts [MTT-4772] (#2222)
* fix Need to track any clients that disconnected to add them to the list of clients that did not complete the scene event. Reducing the logic used to determine when the SceneEventProgress is finished. Renamed a few properties and methods for code readability purposes. Fixed the logic behind determining when the SceneEventProgress should complete. Renaming the SetSceneAsyncOperation to just SetAsyncOperation. * refactor Completely removed the entire SceneEventAction. Replaced it with an even more simplified version of SceneEventProgress. We will no longer allocate memory for SceneEventAction. * style Added additional comments for clarity fixing grammar in a comment removing two CR/LFs moving a comment above a few lines of code (otherwise it doesn't make sense). Last comment adjustment * test Added several tests to verify that SceneEventProgress will complete if all clients finished the scene event, if clients disconnect while it is still in progress, will time out if one or more clients never finish, and finally will still complete if one or more clients late join while it is still in progress.
1 parent 596b941 commit 5da0247

File tree

7 files changed

+363
-147
lines changed

7 files changed

+363
-147
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ Additional documentation and release notes are available at [Multiplayer Documen
2323

2424
- Fixed issue where clients were not rebuilding the `NetworkConfig` hash value for each unique connection request. (#2226)
2525
- Fixed the issue where player objects were not taking the `DontDestroyWithOwner` property into consideration when a client disconnected. (#2225)
26+
- Fixed issue where `SceneEventProgress` would not complete if a client late joins while it is still in progress. (#2222)
27+
- Fixed issue where `SceneEventProgress` would not complete if a client disconnects. (#2222)
28+
- Fixed issues with detecting if a `SceneEventProgress` has timed out. (#2222)
2629
- Fixed issue #1924 where `UnityTransport` would fail to restart after a first failure (even if what caused the initial failure was addressed). (#2220)
2730
- Fixed issue where `NetworkTransform.SetStateServerRpc` and `NetworkTransform.SetStateClientRpc` were not honoring local vs world space settings when applying the position and rotation. (#2203)
2831
- Fixed ILPP `TypeLoadException` on WebGL on MacOS Editor and potentially other platforms. (#2199)
Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using UnityEngine;
32
using UnityEngine.SceneManagement;
43

@@ -10,25 +9,8 @@ namespace Unity.Netcode
109
/// </summary>
1110
internal interface ISceneManagerHandler
1211
{
13-
// Generic action to call when a scene is finished loading/unloading
14-
struct SceneEventAction
15-
{
16-
internal uint SceneEventId;
17-
internal Action<uint> EventAction;
18-
/// <summary>
19-
/// Used server-side for integration testing in order to
20-
/// invoke the SceneEventProgress once done loading
21-
/// </summary>
22-
internal Action Completed;
23-
internal void Invoke()
24-
{
25-
Completed?.Invoke();
26-
EventAction.Invoke(SceneEventId);
27-
}
28-
}
12+
AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress);
2913

30-
AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventAction sceneEventAction);
31-
32-
AsyncOperation UnloadSceneAsync(Scene scene, SceneEventAction sceneEventAction);
14+
AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress);
3315
}
3416
}

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

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,17 @@ public class NetworkSceneManager : IDisposable
338338
/// </summary>
339339
private class DefaultSceneManagerHandler : ISceneManagerHandler
340340
{
341-
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, ISceneManagerHandler.SceneEventAction sceneEventAction)
341+
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress)
342342
{
343343
var operation = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
344-
operation.completed += new Action<AsyncOperation>(asyncOp2 => { sceneEventAction.Invoke(); });
344+
sceneEventProgress.SetAsyncOperation(operation);
345345
return operation;
346346
}
347347

348-
public AsyncOperation UnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEventAction sceneEventAction)
348+
public AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress)
349349
{
350350
var operation = SceneManager.UnloadSceneAsync(scene);
351-
operation.completed += new Action<AsyncOperation>(asyncOp2 => { sceneEventAction.Invoke(); });
351+
sceneEventProgress.SetAsyncOperation(operation);
352352
return operation;
353353
}
354354
}
@@ -887,12 +887,14 @@ private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading
887887
private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress)
888888
{
889889
var sceneEventData = BeginSceneEvent();
890+
var clientsThatCompleted = sceneEventProgress.GetClientsWithStatus(true);
891+
var clientsThatTimedOut = sceneEventProgress.GetClientsWithStatus(false);
890892
sceneEventData.SceneEventProgressId = sceneEventProgress.Guid;
891893
sceneEventData.SceneHash = sceneEventProgress.SceneHash;
892894
sceneEventData.SceneEventType = sceneEventProgress.SceneEventType;
893-
sceneEventData.ClientsCompleted = sceneEventProgress.DoneClients;
895+
sceneEventData.ClientsCompleted = clientsThatCompleted;
894896
sceneEventData.LoadSceneMode = sceneEventProgress.LoadSceneMode;
895-
sceneEventData.ClientsTimedOut = sceneEventProgress.ClientsThatStartedSceneEvent.Except(sceneEventProgress.DoneClients).ToList();
897+
sceneEventData.ClientsTimedOut = clientsThatTimedOut;
896898

897899
var message = new SceneEventMessage
898900
{
@@ -913,8 +915,8 @@ private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress
913915
SceneName = SceneNameFromHash(sceneEventProgress.SceneHash),
914916
ClientId = NetworkManager.ServerClientId,
915917
LoadSceneMode = sceneEventProgress.LoadSceneMode,
916-
ClientsThatCompleted = sceneEventProgress.DoneClients,
917-
ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(),
918+
ClientsThatCompleted = clientsThatCompleted,
919+
ClientsThatTimedOut = clientsThatTimedOut,
918920
});
919921

920922
if (sceneEventData.SceneEventType == SceneEventType.LoadEventCompleted)
@@ -969,18 +971,9 @@ public SceneEventProgressStatus UnloadScene(Scene scene)
969971
sceneEventProgress.SceneEventType = SceneEventType.UnloadEventCompleted;
970972

971973
ScenesLoaded.Remove(scene.handle);
972-
var sceneEventAction = new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventData.SceneEventId, EventAction = OnSceneUnloaded };
973-
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(scene, sceneEventAction);
974-
975-
// If integration testing, IntegrationTestSceneHandler returns null
976-
if (sceneUnload == null)
977-
{
978-
sceneEventProgress.SetSceneLoadOperation(sceneEventAction);
979-
}
980-
else
981-
{
982-
sceneEventProgress.SetSceneLoadOperation(sceneUnload);
983-
}
974+
sceneEventProgress.SceneEventId = sceneEventData.SceneEventId;
975+
sceneEventProgress.OnSceneEventCompleted = OnSceneUnloaded;
976+
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(scene, sceneEventProgress);
984977

985978
// Notify local server that a scene is going to be unloaded
986979
OnSceneEvent?.Invoke(new SceneEvent()
@@ -1024,9 +1017,10 @@ private void OnClientUnloadScene(uint sceneEventId)
10241017
$"because the client scene handle {sceneHandle} was not found in ScenesLoaded!");
10251018
}
10261019
m_IsSceneEventActive = true;
1027-
1028-
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(ScenesLoaded[sceneHandle],
1029-
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventData.SceneEventId, EventAction = OnSceneUnloaded });
1020+
var sceneEventProgress = new SceneEventProgress(m_NetworkManager);
1021+
sceneEventProgress.SceneEventId = sceneEventData.SceneEventId;
1022+
sceneEventProgress.OnSceneEventCompleted = OnSceneUnloaded;
1023+
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(ScenesLoaded[sceneHandle], sceneEventProgress);
10301024

10311025
ScenesLoaded.Remove(sceneHandle);
10321026

@@ -1070,7 +1064,7 @@ private void OnSceneUnloaded(uint sceneEventId)
10701064
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
10711065
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
10721066
{
1073-
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
1067+
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(NetworkManager.ServerClientId);
10741068
}
10751069
}
10761070

@@ -1119,8 +1113,10 @@ internal void UnloadAdditivelyLoadedScenes(uint sceneEventId)
11191113
// Validate the scene as well as ignore the DDOL (which will have a negative buildIndex)
11201114
if (currentActiveScene.name != keyHandleEntry.Value.name && keyHandleEntry.Value.buildIndex >= 0)
11211115
{
1122-
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(keyHandleEntry.Value,
1123-
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = EmptySceneUnloadedOperation });
1116+
var sceneEventProgress = new SceneEventProgress(m_NetworkManager);
1117+
sceneEventProgress.SceneEventId = sceneEventId;
1118+
sceneEventProgress.OnSceneEventCompleted = EmptySceneUnloadedOperation;
1119+
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(keyHandleEntry.Value, sceneEventProgress);
11241120
SceneUnloadEventHandler.RegisterScene(this, keyHandleEntry.Value, LoadSceneMode.Additive, sceneUnload);
11251121
}
11261122
}
@@ -1180,18 +1176,9 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc
11801176
}
11811177

11821178
// Now start loading the scene
1183-
var sceneEventAction = new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = OnSceneLoaded };
1184-
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode, sceneEventAction);
1185-
// If integration testing, IntegrationTestSceneHandler returns null
1186-
if (sceneLoad == null)
1187-
{
1188-
sceneEventProgress.SetSceneLoadOperation(sceneEventAction);
1189-
}
1190-
else
1191-
{
1192-
sceneEventProgress.SetSceneLoadOperation(sceneLoad);
1193-
}
1194-
1179+
sceneEventProgress.SceneEventId = sceneEventId;
1180+
sceneEventProgress.OnSceneEventCompleted = OnSceneLoaded;
1181+
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode, sceneEventProgress);
11951182
// Notify the local server that a scene loading event has begun
11961183
OnSceneEvent?.Invoke(new SceneEvent()
11971184
{
@@ -1355,9 +1342,10 @@ private void OnClientSceneLoadingEvent(uint sceneEventId)
13551342
SceneUnloadEventHandler.RegisterScene(this, SceneManager.GetActiveScene(), LoadSceneMode.Single);
13561343

13571344
}
1358-
1359-
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, sceneEventData.LoadSceneMode,
1360-
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = OnSceneLoaded });
1345+
var sceneEventProgress = new SceneEventProgress(m_NetworkManager);
1346+
sceneEventProgress.SceneEventId = sceneEventId;
1347+
sceneEventProgress.OnSceneEventCompleted = OnSceneLoaded;
1348+
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, sceneEventData.LoadSceneMode, sceneEventProgress);
13611349

13621350
OnSceneEvent?.Invoke(new SceneEvent()
13631351
{
@@ -1488,7 +1476,7 @@ private void OnServerLoadedScene(uint sceneEventId, Scene scene)
14881476
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
14891477
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
14901478
{
1491-
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
1479+
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(NetworkManager.ServerClientId);
14921480
}
14931481
EndSceneEvent(sceneEventId);
14941482
}
@@ -1662,8 +1650,10 @@ private void OnClientBeginSync(uint sceneEventId)
16621650
if (!shouldPassThrough)
16631651
{
16641652
// If not, then load the scene
1665-
sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode,
1666-
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = ClientLoadedSynchronization });
1653+
var sceneEventProgress = new SceneEventProgress(m_NetworkManager);
1654+
sceneEventProgress.SceneEventId = sceneEventId;
1655+
sceneEventProgress.OnSceneEventCompleted = ClientLoadedSynchronization;
1656+
sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode, sceneEventProgress);
16671657

16681658
// Notify local client that a scene load has begun
16691659
OnSceneEvent?.Invoke(new SceneEvent()
@@ -1880,7 +1870,7 @@ private void HandleServerSceneEvent(uint sceneEventId, ulong clientId)
18801870

18811871
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId))
18821872
{
1883-
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(clientId);
1873+
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(clientId);
18841874
}
18851875
EndSceneEvent(sceneEventId);
18861876
break;
@@ -1889,7 +1879,7 @@ private void HandleServerSceneEvent(uint sceneEventId, ulong clientId)
18891879
{
18901880
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId))
18911881
{
1892-
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(clientId);
1882+
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(clientId);
18931883
}
18941884
// Notify the local server that the client has finished unloading a scene
18951885
OnSceneEvent?.Invoke(new SceneEvent()

0 commit comments

Comments
 (0)