Skip to content

Commit 71b76e7

Browse files
fix: OnSceneEvent should raise Unload events for all scenes not just additive scenes [MTT-3580] (#1975)
* fix MTT-3580 This resolves the issue with the active scene and any additively loaded scenes not sending scene event notifications to inform the user that the scenes did, indeed, unload. * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * test - NetworkSceneManagerEventNotifications - update Added some additional tests for switch scene notifications for additively loaded scenes and the currently active scene. * update Minor update to NetworkSceneManager.SceneUnloadEventHandler to deal with the scenario where a scene is unloaded but NetworkManager has already shutdown. * update Final changelog update. MTT-3580 * update Added some shutdown code to make sure SceneUnloadEventHandler cleans up after itself in the event a large scene is in the middle of loading and the player exits the current game-session. Cleaned up some comments. Cleaned up some debug output in the NetworkSceneManagerEventNotifications test.
1 parent 39a2448 commit 71b76e7

File tree

4 files changed

+274
-30
lines changed

4 files changed

+274
-30
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

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

2222
### Fixed
23+
2324
- Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984)
25+
- Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975)
2426
- 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)
2527
- Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972)
2628
- `FixedString` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper (#1961)

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

Lines changed: 126 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,12 @@ public class NetworkSceneManager : IDisposable
329329
public VerifySceneBeforeLoadingDelegateHandler VerifySceneBeforeLoading;
330330

331331
/// <summary>
332-
/// Proof of concept: Interface version that allows for the decoupling from
333-
/// the SceneManager's Load and Unload methods for testing purposes (potentially other future usage)
332+
/// The SceneManagerHandler implementation
333+
/// </summary>
334+
internal ISceneManagerHandler SceneManagerHandler = new DefaultSceneManagerHandler();
335+
336+
/// <summary>
337+
/// The default SceneManagerHandler that interfaces between the SceneManager and NetworkSceneManager
334338
/// </summary>
335339
private class DefaultSceneManagerHandler : ISceneManagerHandler
336340
{
@@ -349,10 +353,6 @@ public AsyncOperation UnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEv
349353
}
350354
}
351355

352-
internal ISceneManagerHandler SceneManagerHandler = new DefaultSceneManagerHandler();
353-
/// End of Proof of Concept
354-
355-
356356
internal readonly Dictionary<Guid, SceneEventProgress> SceneEventProgressTracking = new Dictionary<Guid, SceneEventProgress>();
357357

358358
/// <summary>
@@ -435,6 +435,8 @@ public AsyncOperation UnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEv
435435
/// </summary>
436436
public void Dispose()
437437
{
438+
SceneUnloadEventHandler.Shutdown();
439+
438440
foreach (var keypair in SceneEventDataStore)
439441
{
440442
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
@@ -882,7 +884,6 @@ private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading
882884
/// Callback for the <see cref="SceneEventProgress.OnComplete"/> <see cref="SceneEventProgress.OnCompletedDelegate"/> handler
883885
/// </summary>
884886
/// <param name="sceneEventProgress"></param>
885-
/// <returns></returns>
886887
private bool OnSceneEventProgressCompleted(SceneEventProgress sceneEventProgress)
887888
{
888889
var sceneEventData = BeginSceneEvent();
@@ -981,7 +982,6 @@ public SceneEventProgressStatus UnloadScene(Scene scene)
981982
sceneEventProgress.SetSceneLoadOperation(sceneUnload);
982983
}
983984

984-
985985
// Notify local server that a scene is going to be unloaded
986986
OnSceneEvent?.Invoke(new SceneEvent()
987987
{
@@ -1101,7 +1101,7 @@ private void OnSceneUnloaded(uint sceneEventId)
11011101

11021102
private void EmptySceneUnloadedOperation(uint sceneEventId)
11031103
{
1104-
// Do nothing (this is a stub call since it is only used to flush all currently loaded scenes)
1104+
// Do nothing (this is a stub call since it is only used to flush all additively loaded scenes)
11051105
}
11061106

11071107
/// <summary>
@@ -1111,6 +1111,7 @@ private void EmptySceneUnloadedOperation(uint sceneEventId)
11111111
/// </summary>
11121112
internal void UnloadAdditivelyLoadedScenes(uint sceneEventId)
11131113
{
1114+
var sceneEventData = SceneEventDataStore[sceneEventId];
11141115
// Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ).
11151116
var currentActiveScene = SceneManager.GetActiveScene();
11161117
foreach (var keyHandleEntry in ScenesLoaded)
@@ -1120,15 +1121,7 @@ internal void UnloadAdditivelyLoadedScenes(uint sceneEventId)
11201121
{
11211122
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(keyHandleEntry.Value,
11221123
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = EmptySceneUnloadedOperation });
1123-
1124-
OnSceneEvent?.Invoke(new SceneEvent()
1125-
{
1126-
AsyncOperation = sceneUnload,
1127-
SceneEventType = SceneEventType.Unload,
1128-
SceneName = keyHandleEntry.Value.name,
1129-
LoadSceneMode = LoadSceneMode.Additive, // The only scenes unloaded are scenes that were additively loaded
1130-
ClientId = NetworkManager.ServerClientId
1131-
});
1124+
SceneUnloadEventHandler.RegisterScene(this, keyHandleEntry.Value, LoadSceneMode.Additive, sceneUnload);
11321125
}
11331126
}
11341127
// clear out our scenes loaded list
@@ -1180,12 +1173,14 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc
11801173

11811174
// Now Unload all currently additively loaded scenes
11821175
UnloadAdditivelyLoadedScenes(sceneEventId);
1176+
1177+
// Register the active scene for unload scene event notifications
1178+
SceneUnloadEventHandler.RegisterScene(this, SceneManager.GetActiveScene(), LoadSceneMode.Single);
11831179
}
11841180

11851181
// Now start loading the scene
11861182
var sceneEventAction = new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = OnSceneLoaded };
11871183
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode, sceneEventAction);
1188-
11891184
// If integration testing, IntegrationTestSceneHandler returns null
11901185
if (sceneLoad == null)
11911186
{
@@ -1212,6 +1207,114 @@ public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSc
12121207
return sceneEventProgress.Status;
12131208
}
12141209

1210+
/// <summary>
1211+
/// Helper class used to handle "odd ball" scene unload event notification scenarios
1212+
/// when scene switching.
1213+
/// </summary>
1214+
internal class SceneUnloadEventHandler
1215+
{
1216+
private static Dictionary<NetworkManager, List<SceneUnloadEventHandler>> s_Instances = new Dictionary<NetworkManager, List<SceneUnloadEventHandler>>();
1217+
1218+
internal static void RegisterScene(NetworkSceneManager networkSceneManager, Scene scene, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation = null)
1219+
{
1220+
var networkManager = networkSceneManager.m_NetworkManager;
1221+
if (!s_Instances.ContainsKey(networkManager))
1222+
{
1223+
s_Instances.Add(networkManager, new List<SceneUnloadEventHandler>());
1224+
}
1225+
var clientId = networkManager.IsServer ? NetworkManager.ServerClientId : networkManager.LocalClientId;
1226+
s_Instances[networkManager].Add(new SceneUnloadEventHandler(networkSceneManager, scene, clientId, loadSceneMode, asyncOperation));
1227+
}
1228+
1229+
private static void SceneUnloadComplete(SceneUnloadEventHandler sceneUnloadEventHandler)
1230+
{
1231+
if (sceneUnloadEventHandler == null || sceneUnloadEventHandler.m_NetworkSceneManager == null || sceneUnloadEventHandler.m_NetworkSceneManager.m_NetworkManager == null)
1232+
{
1233+
return;
1234+
}
1235+
var networkManager = sceneUnloadEventHandler.m_NetworkSceneManager.m_NetworkManager;
1236+
if (s_Instances.ContainsKey(networkManager))
1237+
{
1238+
s_Instances[networkManager].Remove(sceneUnloadEventHandler);
1239+
if (s_Instances[networkManager].Count == 0)
1240+
{
1241+
s_Instances.Remove(networkManager);
1242+
}
1243+
}
1244+
}
1245+
1246+
/// <summary>
1247+
/// Called by NetworkSceneManager when it is disposing
1248+
/// </summary>
1249+
internal static void Shutdown()
1250+
{
1251+
foreach (var instanceEntry in s_Instances)
1252+
{
1253+
foreach (var instance in instanceEntry.Value)
1254+
{
1255+
instance.OnShutdown();
1256+
}
1257+
instanceEntry.Value.Clear();
1258+
}
1259+
s_Instances.Clear();
1260+
}
1261+
1262+
private NetworkSceneManager m_NetworkSceneManager;
1263+
private AsyncOperation m_AsyncOperation;
1264+
private LoadSceneMode m_LoadSceneMode;
1265+
private ulong m_ClientId;
1266+
private Scene m_Scene;
1267+
private bool m_ShuttingDown;
1268+
1269+
private void OnShutdown()
1270+
{
1271+
m_ShuttingDown = true;
1272+
SceneManager.sceneUnloaded -= SceneUnloaded;
1273+
}
1274+
1275+
private void SceneUnloaded(Scene scene)
1276+
{
1277+
if (m_Scene.handle == scene.handle && !m_ShuttingDown)
1278+
{
1279+
if (m_NetworkSceneManager != null && m_NetworkSceneManager.m_NetworkManager != null)
1280+
{
1281+
m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent()
1282+
{
1283+
AsyncOperation = m_AsyncOperation,
1284+
SceneEventType = SceneEventType.UnloadComplete,
1285+
SceneName = m_Scene.name,
1286+
LoadSceneMode = m_LoadSceneMode,
1287+
ClientId = m_ClientId
1288+
});
1289+
m_NetworkSceneManager.OnUnloadComplete?.Invoke(m_ClientId, m_Scene.name);
1290+
}
1291+
SceneManager.sceneUnloaded -= SceneUnloaded;
1292+
SceneUnloadComplete(this);
1293+
}
1294+
}
1295+
1296+
private SceneUnloadEventHandler(NetworkSceneManager networkSceneManager, Scene scene, ulong clientId, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation = null)
1297+
{
1298+
m_LoadSceneMode = loadSceneMode;
1299+
m_AsyncOperation = asyncOperation;
1300+
m_NetworkSceneManager = networkSceneManager;
1301+
m_ClientId = clientId;
1302+
m_Scene = scene;
1303+
SceneManager.sceneUnloaded += SceneUnloaded;
1304+
// Send the initial unload event notification
1305+
m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent()
1306+
{
1307+
AsyncOperation = m_AsyncOperation,
1308+
SceneEventType = SceneEventType.Unload,
1309+
SceneName = m_Scene.name,
1310+
LoadSceneMode = m_LoadSceneMode,
1311+
ClientId = clientId
1312+
});
1313+
1314+
m_NetworkSceneManager.OnUnload?.Invoke(networkSceneManager.m_NetworkManager.LocalClientId, m_Scene.name, null);
1315+
}
1316+
}
1317+
12151318
/// <summary>
12161319
/// Client Side:
12171320
/// Handles both forms of scene loading
@@ -1246,6 +1349,10 @@ private void OnClientSceneLoadingEvent(uint sceneEventId)
12461349
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
12471350
{
12481351
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
1352+
1353+
// Register the active scene for unload scene event notifications
1354+
SceneUnloadEventHandler.RegisterScene(this, SceneManager.GetActiveScene(), LoadSceneMode.Single);
1355+
12491356
}
12501357

12511358
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, sceneEventData.LoadSceneMode,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ static internal IEnumerator ProcessUnloadingSceneJob(QueuedSceneJob queuedSceneJ
174174
}
175175

176176
SceneManager.sceneUnloaded += SceneManager_sceneUnloaded;
177-
if (queuedSceneJob.Scene.IsValid() && queuedSceneJob.Scene.isLoaded)
177+
if (queuedSceneJob.Scene.IsValid() && queuedSceneJob.Scene.isLoaded && !queuedSceneJob.Scene.name.Contains(NetcodeIntegrationTestHelpers.FirstPartOfTestRunnerSceneName))
178178
{
179179
SceneManager.UnloadSceneAsync(queuedSceneJob.Scene);
180180
}

0 commit comments

Comments
 (0)