Skip to content

Commit 846065a

Browse files
fix: server not tracking scene handles for pre-loaded scenes (#4146)
Scenes that were already loaded when a session starts were added to ScenesLoaded but never to the server-to-client scene handle tables, so UnloadScene passed its ScenesLoaded check and then failed inside RemoveServerClientSceneHandle. The scene unloaded on every peer, but an error was logged and the ScenesLoaded entry was never removed. The client-server branch of the NetworkSceneManager constructor now registers those scenes the same way InitializeScenesLoaded does for distributed authority. NetcodeIntegrationTestHelpers registered the test runner scene in ServerSceneHandleToClientSceneHandle unconditionally, which now collides with the constructor already having added it, so that add is guarded.
1 parent 6970080 commit 846065a

4 files changed

Lines changed: 35 additions & 2 deletions

File tree

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2929

3030
### Fixed
3131

32+
- 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)
3233
- Issue with not being able to spawn initially disabled in-scene placed objects. (#4093)
3334
- Issue with pre-instantiated network prefab instances being marked as in-scene placed. Now pre-instantiated network prefabs are dynamically spawned. (#4093)
3435
- 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)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ internal NetworkSceneManager(NetworkManager networkManager)
847847
for (int i = 0; i < SceneManager.sceneCount; i++)
848848
{
849849
var loadedScene = SceneManager.GetSceneAt(i);
850-
ScenesLoaded.Add(loadedScene.handle, loadedScene);
850+
UpdateServerClientSceneHandle(loadedScene.handle, loadedScene.handle, loadedScene);
851851
}
852852
SceneManagerHandler.PopulateLoadedScenes(ref ScenesLoaded, NetworkManager);
853853
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,9 @@ private static void SceneManagerValidationAndTestRunnerInitialization(NetworkMan
540540
}
541541
return;
542542
}
543-
networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.Add(scene.handle, scene.handle);
543+
544+
// The server already registers every scene loaded prior to startup, so only add the test runner scene if it is not already there.
545+
networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.TryAdd(scene.handle, scene.handle);
544546
}
545547
}
546548

testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerStartupTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal class NetworkSceneManagerStartupTests : NetcodeIntegrationTest
2121
{
2222
private const string k_ActiveScene = "SessionSynchronize";
2323
private const string k_AdditionalScene = "InSceneNetworkObjectMovesToDDOL";
24+
private const string k_PreLoadedScene = "EmptyScene1";
2425

2526
private readonly List<NetworkObject> m_ObjectsInScenes = new List<NetworkObject>();
2627
private Scene m_OriginalActiveScene;
@@ -141,6 +142,35 @@ public IEnumerator AllExistingObjectsAreSpawnedAtStartup([Values] LoadSceneMode
141142
AssertOnTimeout("Timed out waiting for objects to spawn on all clients!");
142143
}
143144

145+
/// <summary>
146+
/// Validates that a scene additively loaded before the session started is tracked well enough
147+
/// to be unloaded through <see cref="NetworkSceneManager"/> without error.
148+
/// </summary>
149+
[UnityTest]
150+
public IEnumerator UnloadPreLoadedScene()
151+
{
152+
yield return PreLoadScene(k_PreLoadedScene);
153+
var preLoadedScene = m_SceneLoaded;
154+
155+
m_CanStart = true;
156+
yield return StartServerAndClients();
157+
158+
// Scenes loaded before the session started are registered in both the loaded scenes and the
159+
// scene handle tables, otherwise unloading them fails part way through and leaks the entry.
160+
var sceneManager = GetAuthorityNetworkManager().SceneManager;
161+
Assert.IsTrue(sceneManager.ScenesLoaded.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} is not in {nameof(NetworkSceneManager.ScenesLoaded)}!");
162+
Assert.IsTrue(sceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} is not in {nameof(NetworkSceneManager.ServerSceneHandleToClientSceneHandle)}!");
163+
164+
var status = sceneManager.UnloadScene(preLoadedScene);
165+
Assert.AreEqual(SceneEventProgressStatus.Started, status, $"{nameof(NetworkSceneManager.UnloadScene)} returned {status}!");
166+
167+
yield return WaitForConditionOrTimeOut(() => !preLoadedScene.isLoaded);
168+
AssertOnTimeout($"Timed out waiting for {k_PreLoadedScene} to unload!");
169+
170+
Assert.IsFalse(sceneManager.ScenesLoaded.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} was unloaded but is still in {nameof(NetworkSceneManager.ScenesLoaded)}!");
171+
Assert.IsFalse(sceneManager.ServerSceneHandleToClientSceneHandle.ContainsKey(preLoadedScene.handle), $"{k_PreLoadedScene} was unloaded but is still in {nameof(NetworkSceneManager.ServerSceneHandleToClientSceneHandle)}!");
172+
}
173+
144174
#region Scene loading and related methods
145175

146176
/// <summary>

0 commit comments

Comments
 (0)