From d8d2da23dc6580ec64460534b8809a5dd98ef202 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 1 Jul 2026 17:58:57 -0500 Subject: [PATCH 1/4] fix - NetworkObject null exception on warning log If the NetworkObject was not found then use "null" as the name of the NetworkObject. --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 3423c05a07..02244f69ec 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -3479,7 +3479,8 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject, { if (networkManager.LogLevel <= LogLevel.Normal) { - NetworkLog.LogWarning($"[{networkObject.name}][Deserialize][{nameof(NetworkBehaviour)}Synchronization][Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!"); + var networkObjectName = networkObject != null ? networkObject.name : "null"; + NetworkLog.LogWarning($"[{networkObjectName}][Deserialize][{nameof(NetworkBehaviour)}Synchronization][Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!"); } reader.Seek(endOfSynchronizationData); } From 2956fcfe7f148ffb1be87a6553c8d5c2c2aab737 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 1 Jul 2026 18:05:20 -0500 Subject: [PATCH 2/4] fix - synchronizing clients should always load active scene first This fixes the issue where the active scene could end up not being the 1st scene loaded which upon loading the active scene and previously SceneEventData relative loaded scenes would get unloaded when loading the active scene since this will can end up being loaded in SingleMode. --- .../SceneManagement/NetworkSceneManager.cs | 90 ++++++++++--------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 8c07fbd603..61bc5ee896 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -1952,6 +1952,21 @@ private void OnClientLoadedScene(uint sceneEventId, Scene scene) /// internal List ClientConnectionQueue = new List(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddSceneToClientSynchronization(ref SceneEventData sceneEventData, ref Scene scene) + { + // If we are just a normal client and in distributed authority mode, then always use the known server scene handle + if (NetworkManager.DistributedAuthorityMode && NetworkManager.CMBServiceConnection) + { + sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), ClientSceneHandleToServerSceneHandle[scene.handle]); + } + else + { + sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), scene.handle); + } + } + /// /// Server Side: /// This is used for players that have just had their connection approved and will assure they are synchronized @@ -2002,61 +2017,53 @@ internal void SynchronizeNetworkObjects(ulong clientId, bool synchronizingServic // Organize how (and when) we serialize our NetworkObjects var hasSynchronizedActive = false; - for (int i = 0; i < SceneManager.sceneCount; i++) - { - var scene = SceneManager.GetSceneAt(i); - // NetworkSceneManager does not synchronize scenes that are not loaded by NetworkSceneManager - // unless the scene in question is the currently active scene. - if (ExcludeSceneFromSychronization != null && !ExcludeSceneFromSychronization(scene)) + // It is possible a user might not want to synchronize the active scene, so we will check to see if it is valid before adding it to the synchronization list. + // !! Important !! + // The active scene MUST always be the first scene in the synchronization list. + if (ValidateSceneBeforeLoading(activeScene.buildIndex, activeScene.name, sceneEventData.LoadSceneMode)) + { + sceneEventData.SceneHash = SceneHashFromNameOrPath(activeScene.path); + if (sceneEventData.SceneHash == sceneEventData.ActiveSceneHash) { - continue; + hasSynchronizedActive = true; } - if (scene == DontDestroyOnLoadScene) + // If we are just a normal client, then always use the server scene handle + if (NetworkManager.DistributedAuthorityMode) { - continue; + sceneEventData.SenderClientId = NetworkManager.LocalClientId; + sceneEventData.SceneHandle = ClientSceneHandleToServerSceneHandle[activeScene.handle]; } - - // This would depend upon whether we are additive or not - // If we are the base scene, then we set the root scene index; - if (activeScene == scene) + else { - if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, sceneEventData.LoadSceneMode)) - { - continue; - } - sceneEventData.SceneHash = SceneHashFromNameOrPath(scene.path); - if (sceneEventData.SceneHash == sceneEventData.ActiveSceneHash) - { - hasSynchronizedActive = true; - } - - // If we are just a normal client, then always use the server scene handle - if (NetworkManager.DistributedAuthorityMode) - { - sceneEventData.SenderClientId = NetworkManager.LocalClientId; - sceneEventData.SceneHandle = ClientSceneHandleToServerSceneHandle[scene.handle]; - } - else - { - sceneEventData.SceneHandle = scene.handle; - } + sceneEventData.SceneHandle = activeScene.handle; } - else if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, LoadSceneMode.Additive)) + AddSceneToClientSynchronization(ref sceneEventData, ref activeScene); + } + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + // Skip adding the active scene at this point as we are just adding all other additively loaded scenes to the synchronization list. + // Skip adding the dont destroy on load scene as that is never synchronized. + if ((scene.handle == activeScene.handle) || (scene == DontDestroyOnLoadScene)) { continue; } - // If we are just a normal client and in distributed authority mode, then always use the known server scene handle - if (NetworkManager.DistributedAuthorityMode && NetworkManager.CMBServiceConnection) + // NetworkSceneManager does not synchronize scenes that are not loaded by NetworkSceneManager + // unless the scene in question is the currently active scene. + if (ExcludeSceneFromSychronization != null && !ExcludeSceneFromSychronization(scene)) { - sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), ClientSceneHandleToServerSceneHandle[scene.handle]); + continue; } - else + + if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, LoadSceneMode.Additive)) { - sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), scene.handle); + continue; } + AddSceneToClientSynchronization(ref sceneEventData, ref scene); } if (!hasSynchronizedActive && NetworkManager.CMBServiceConnection && synchronizingService) @@ -2109,9 +2116,12 @@ private void OnClientBeginSync(uint sceneEventId) var sceneHash = sceneEventData.GetNextSceneSynchronizationHash(); var sceneHandle = sceneEventData.GetNextSceneSynchronizationHandle(); var sceneName = SceneNameFromHash(sceneHash); + var activeSceneName = SceneNameFromHash(sceneEventData.ActiveSceneHash); var activeScene = SceneManager.GetActiveScene(); - var loadSceneMode = sceneHash == sceneEventData.SceneHash ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive; + var activeSceneLoaded = activeSceneName == activeScene.name; + + var loadSceneMode = sceneHash == sceneEventData.SceneHash && !activeSceneLoaded ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive; // Store the sceneHandle and hash sceneEventData.NetworkSceneHandle = sceneHandle; From 5703c755aa4c67bef70688f875da67bd0f564c30 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 1 Jul 2026 18:02:27 -0500 Subject: [PATCH 3/4] fix - prevent marking dynamically spawned objects as in-scene This fix just ignores any spawned objects when setting any just loaded InScenePlaced objects while running in the editor. --- .../Editor/InScenePlacedProcessor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs index 423ff4a65c..2b41177e5b 100644 --- a/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs +++ b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs @@ -21,6 +21,11 @@ public void OnProcessScene(Scene scene, BuildReport report) { foreach (var networkObject in FindObjects.FromSceneByType(scene, true)) { + // Exclude any already spawned objects found since this + if (networkObject.IsSpawned) + { + continue; + } networkObject.InScenePlaced = true; } } From b56ccdc435ff01af241d4d0548a3ed194179dac1 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 1 Jul 2026 18:23:43 -0500 Subject: [PATCH 4/4] style finishing an incomplete comment --- com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs index 2b41177e5b..71a04bff2e 100644 --- a/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs +++ b/com.unity.netcode.gameobjects/Editor/InScenePlacedProcessor.cs @@ -21,7 +21,8 @@ public void OnProcessScene(Scene scene, BuildReport report) { foreach (var networkObject in FindObjects.FromSceneByType(scene, true)) { - // Exclude any already spawned objects found since this + // Exclude any already spawned objects found since this executes after the NetworkSceneManager has finished + // migrating anything from the DDOL into the newly loaded scene. if (networkObject.IsSpawned) { continue;