From 9571eda51f03a4905e916b85eda723cb75376990 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Mon, 27 Jul 2026 14:59:22 -0500 Subject: [PATCH 1/2] fix This resolves the issue where in a distributed authority session changing a NetworkVariable prior to changing ownership in the same call-stack would result in the NetworkVariable not being synchronized. --- .../Runtime/Core/NetworkObject.cs | 30 ++++++++++++++----- .../Messages/ChangeOwnershipMessage.cs | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 52e4dad4f1..c1cca0c5d9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2961,7 +2961,7 @@ internal bool InitializeChildNetworkBehaviours() /// /// the owner prior to beginning the change in ownership change. /// the previous owner prior to beginning the change in ownership change. - internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong originalPreviousOwnerId) + internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong originalPreviousOwnerId, bool authorityChangedOwnership = false) { var currentOwnerId = OwnerClientId; OwnerClientId = originalOwnerId; @@ -2971,17 +2971,31 @@ internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong orig childBehaviour.MarkOwnerReadDirtyAndCheckOwnerWriteIsDirty(); } + // If the spawn authority of a distributed authority network topology has invoked a change in ownership, + // then we want to invoke the NetworkBehaviourUpdate prior to changing the owner back. + if (authorityChangedOwnership && NetworkManager.DistributedAuthorityMode) + { + // Force send a state update for all owner read NetworkVariables and any currently dirty + // owner write NetworkVariables. + NetworkManagerOwner.BehaviourUpdater.NetworkBehaviourUpdate(true); + } + // Now set the new owner and previous owner identifiers back to their original new values - // before we run the NetworkBehaviourUpdate. For owner read only permissions this order of - // operations is **particularly important** as we need to first (above) mark things as dirty - // from the context of the original owner and then second (below) we need to send the messages - // which requires the new owner to be set for owner read permission NetworkVariables. + // after we run the NetworkBehaviourUpdate. OwnerClientId = currentOwnerId; PreviousOwnerId = originalOwnerId; - // Force send a state update for all owner read NetworkVariables and any currently dirty - // owner write NetworkVariables. - NetworkManagerOwner.BehaviourUpdater.NetworkBehaviourUpdate(true); + // For owner read only permissions this order of operations is **particularly important** as + // we need to first (above) mark things as dirty from the context of the original owner and + // then second (below) we need to send the messages which requires the new owner to be set. + // Note: Owner read only NetworkVariables do not make sense in a distributed authority topology + // since the only instance that can write is the owner. + if (!NetworkManager.DistributedAuthorityMode) + { + // Force send a state update for all owner read NetworkVariables and any currently dirty + // owner write NetworkVariables. + NetworkManagerOwner.BehaviourUpdater.NetworkBehaviourUpdate(true); + } } // NGO currently guarantees that the client will receive spawn data for all objects in one network tick. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs index eb54a25d63..1a182ea01d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -241,7 +241,7 @@ private void HandleOwnershipChange(ref NetworkContext context, ref NetworkManage if (!distributedAuthorityMode && originalOwner == networkManager.LocalClientId) { // Fully synchronize NetworkVariables with either read or write ownership permissions. - networkObject.SynchronizeOwnerNetworkVariables(originalOwner, networkObject.PreviousOwnerId); + networkObject.SynchronizeOwnerNetworkVariables(originalOwner, networkObject.PreviousOwnerId, true); } // Always invoke ownership change notifications From 63301e004938e8cb97ca8eef27c64833daf8c0e9 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Mon, 27 Jul 2026 17:25:47 -0500 Subject: [PATCH 2/2] fix - update Removing the additional authority check as it is not needed. Fixing a very edge case scenario that shouldn't happen but in the event it does when finding objects by type within a specific scene, if the scene is invalid then use an empty array for the found objects. --- com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs | 4 ++-- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 4 ++-- .../Runtime/Messaging/Messages/ChangeOwnershipMessage.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs b/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs index 59329f8a81..109d40f4d3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/FindObjects.cs @@ -69,8 +69,8 @@ private struct ObjectsInSceneEnumerator : IEnumerable, IEnumerator wher internal ObjectsInSceneEnumerator(Scene scene, bool includeInactive) { m_IncludeInactive = includeInactive; - - m_RootObjects = scene.GetRootGameObjects(); + // If the scene is invalid, use an empty array. + m_RootObjects = scene.IsValid() ? scene.GetRootGameObjects() : new UnityEngine.GameObject[0]; m_RootIndex = 0; m_CurrentChildObjects = null; m_CurrentChildIndex = 0; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index c1cca0c5d9..6905caf3b2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2961,7 +2961,7 @@ internal bool InitializeChildNetworkBehaviours() /// /// the owner prior to beginning the change in ownership change. /// the previous owner prior to beginning the change in ownership change. - internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong originalPreviousOwnerId, bool authorityChangedOwnership = false) + internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong originalPreviousOwnerId) { var currentOwnerId = OwnerClientId; OwnerClientId = originalOwnerId; @@ -2973,7 +2973,7 @@ internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong orig // If the spawn authority of a distributed authority network topology has invoked a change in ownership, // then we want to invoke the NetworkBehaviourUpdate prior to changing the owner back. - if (authorityChangedOwnership && NetworkManager.DistributedAuthorityMode) + if (NetworkManager.DistributedAuthorityMode) { // Force send a state update for all owner read NetworkVariables and any currently dirty // owner write NetworkVariables. diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs index 1a182ea01d..eb54a25d63 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs @@ -241,7 +241,7 @@ private void HandleOwnershipChange(ref NetworkContext context, ref NetworkManage if (!distributedAuthorityMode && originalOwner == networkManager.LocalClientId) { // Fully synchronize NetworkVariables with either read or write ownership permissions. - networkObject.SynchronizeOwnerNetworkVariables(originalOwner, networkObject.PreviousOwnerId, true); + networkObject.SynchronizeOwnerNetworkVariables(originalOwner, networkObject.PreviousOwnerId); } // Always invoke ownership change notifications