diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 85157fe510..65db23ecf2 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -29,6 +29,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed issue where scenes additively loaded before a session started were tracked as loaded on the server but had no scene handle entries, which caused `NetworkSceneManager.UnloadScene` to log an error and leave the scene registered as loaded even though it unloaded on all peers. (#4146) - Issue with not being able to spawn initially disabled in-scene placed objects. (#4093) - Issue with pre-instantiated network prefab instances being marked as in-scene placed. Now pre-instantiated network prefabs are dynamically spawned. (#4093) - Issue where a user could spawn runtime created `NetworkObject` that has a GlobalObjectIdHash of zero. These are not valid instances and will no longer be allowed to spawn. (#4093) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index b0db598848..15f8b5afe4 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -847,7 +847,7 @@ internal NetworkSceneManager(NetworkManager networkManager) for (int i = 0; i < SceneManager.sceneCount; i++) { var loadedScene = SceneManager.GetSceneAt(i); - ScenesLoaded.Add(loadedScene.handle, loadedScene); + UpdateServerClientSceneHandle(loadedScene.handle, loadedScene.handle, loadedScene); } SceneManagerHandler.PopulateLoadedScenes(ref ScenesLoaded, NetworkManager); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs index 8f32fc1916..02b2503fbd 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs @@ -540,7 +540,9 @@ private static void SceneManagerValidationAndTestRunnerInitialization(NetworkMan } return; } - networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.Add(scene.handle, scene.handle); + + // The server already registers every scene loaded prior to startup, so only add the test runner scene if it is not already there. + networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.TryAdd(scene.handle, scene.handle); } } diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs index 28cd0b49d2..968f63e9dc 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs @@ -21,6 +21,7 @@ internal class NetworkSceneManagerStartupTests : NetcodeIntegrationTest { private const string k_ActiveScene = "SessionSynchronize"; private const string k_AdditionalScene = "InSceneNetworkObjectMovesToDDOL"; + private const string k_PreLoadedScene = "EmptyScene1"; private readonly List m_ObjectsInScenes = new List(); private Scene m_OriginalActiveScene; @@ -141,6 +142,35 @@ public IEnumerator AllExistingObjectsAreSpawnedAtStartup([Values] LoadSceneMode AssertOnTimeout("Timed out waiting for objects to spawn on all clients!"); } + /// + /// Validates that a scene additively loaded before the session started is tracked well enough + /// to be unloaded through without error. + /// + [UnityTest] + public IEnumerator UnloadPreLoadedScene() + { + yield return PreLoadScene(k_PreLoadedScene); + var preLoadedScene = m_SceneLoaded; + + m_CanStart = true; + yield return StartServerAndClients(); + + // Scenes loaded before the session started are registered in both the loaded scenes and the + // scene handle tables, otherwise unloading them fails part way through and leaks the entry. + var sceneManager = GetAuthorityNetworkManager().SceneManager; + Assert.IsTrue(sceneManager.ScenesLoaded.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} is not in {nameof(NetworkSceneManager.ScenesLoaded)}!"); + Assert.IsTrue(sceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} is not in {nameof(NetworkSceneManager.ServerSceneHandleToClientSceneHandle)}!"); + + var status = sceneManager.UnloadScene(preLoadedScene); + Assert.AreEqual(SceneEventProgressStatus.Started, status, $"{nameof(NetworkSceneManager.UnloadScene)} returned {status}!"); + + yield return WaitForConditionOrTimeOut(() => !preLoadedScene.isLoaded); + AssertOnTimeout($"Timed out waiting for {k_PreLoadedScene} to unload!"); + + Assert.IsFalse(sceneManager.ScenesLoaded.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} was unloaded but is still in {nameof(NetworkSceneManager.ScenesLoaded)}!"); + Assert.IsFalse(sceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} was unloaded but is still in {nameof(NetworkSceneManager.ServerSceneHandleToClientSceneHandle)}!"); + } + #region Scene loading and related methods ///