From 511af606f73172ebf68df5ffc7b1c239be9f8b69 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 20 Jan 2026 18:41:20 -0600 Subject: [PATCH 01/10] fix Only the spawn authority should reset the sent time and reset the dirty flag for NetworkList changes to avoid sending both the CreateObjectMessage (which would contain the changes) plus the NetworkVariableDeltaMessage. --- .../Runtime/NetworkVariable/Collections/NetworkList.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index 42bc1b22d0..da191be04f 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -61,10 +61,12 @@ public NetworkList(IEnumerable values = default, internal override void OnSpawned() { // If we are dirty and have write permissions by the time the NetworkObject - // is finished spawning (same frame), then go ahead and reset the dirty related - // properties for NetworkList in the event user script has made changes when - // spawning to prevent duplicate entries. - if (IsDirty() && CanSend()) + // is finished spawning (same frame) and the instance is on the spawn authority + // side, then go ahead and reset the dirty related properties for NetworkList + // in the event user script has made changes when spawning to prevent duplicate + // entries (i.e. they are sent via CreateObjectMessage so we don't need to send + // the NetworkVariableDeltaMessage. + if (IsDirty() && CanSend() && m_NetworkBehaviour.HasAuthority) { UpdateLastSentTime(); ResetDirty(); From d09a7c9fbe9623d91c70b3c9146066b366acbf27 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 21 Jan 2026 00:12:54 -0600 Subject: [PATCH 02/10] style updating comment in area updated for further clarity on the requirements to resetting the dirty and last sent time after the associated NetworkObject has been spawned. --- .../NetworkVariable/Collections/NetworkList.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index da191be04f..bf69446dbf 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -60,12 +60,17 @@ public NetworkList(IEnumerable values = default, internal override void OnSpawned() { - // If we are dirty and have write permissions by the time the NetworkObject - // is finished spawning (same frame) and the instance is on the spawn authority - // side, then go ahead and reset the dirty related properties for NetworkList - // in the event user script has made changes when spawning to prevent duplicate - // entries (i.e. they are sent via CreateObjectMessage so we don't need to send - // the NetworkVariableDeltaMessage. + // If the NetworkList is: + // - Dirty + // - State updates can be sent: + // -- The instance has write permissions. + // -- The last sent time plus the max send time period is less than the current time. + // - User script has modified the list during spawn. + // - This instance is on the spawn authority side. + // Then by the time the NetworkObject is finished spawning (on the same frame), then go + // ahead and reset the dirty related properties and last sent time to prevent duplicate + // entries from being sent (i.e. CreateObjectMessage will contain the changes so we + // don't need to send a proceeding NetworkVariableDeltaMessage). if (IsDirty() && CanSend() && m_NetworkBehaviour.HasAuthority) { UpdateLastSentTime(); From ccd87bc5d81187076906d0ee3e6d09a6fe3507d6 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 21 Jan 2026 00:21:58 -0600 Subject: [PATCH 03/10] update Adding change log entry. --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 30247ba839..5bfa2d2c0f 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -24,8 +24,10 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed issue when using a client-server topology where a NetworkList with owner write permissions was resetting sent time and dirty flags on owning clients that were not the spawn authority. (#3850) - Fixed an integer overflow that occurred when configuring a large disconnect timeout with Unity Transport. (#3810) + ### Security From 709f6d063a24ce61bda5eca01c5ff28ddcbc4332 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 21 Jan 2026 00:23:46 -0600 Subject: [PATCH 04/10] style Rephrasing change log entry. --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 5bfa2d2c0f..dd1c9347e2 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -24,7 +24,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed issue when using a client-server topology where a NetworkList with owner write permissions was resetting sent time and dirty flags on owning clients that were not the spawn authority. (#3850) +- Fixed issue when using a client-server topology where a `NetworkList` with owner write permissions was resetting sent time and dirty flags after having been spawned on owning clients that were not the spawn authority. (#3850) - Fixed an integer overflow that occurred when configuring a large disconnect timeout with Unity Transport. (#3810) From 2f6df55e3eeb83f5e7f5675480f0629de1c9e19c Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 21 Jan 2026 00:27:16 -0600 Subject: [PATCH 05/10] style One last adjustment to the comment over the area fixed. --- .../Runtime/NetworkVariable/Collections/NetworkList.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index bf69446dbf..2b2410dc67 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -67,10 +67,10 @@ internal override void OnSpawned() // -- The last sent time plus the max send time period is less than the current time. // - User script has modified the list during spawn. // - This instance is on the spawn authority side. - // Then by the time the NetworkObject is finished spawning (on the same frame), then go - // ahead and reset the dirty related properties and last sent time to prevent duplicate - // entries from being sent (i.e. CreateObjectMessage will contain the changes so we - // don't need to send a proceeding NetworkVariableDeltaMessage). + // When the NetworkObject is finished spawning (on the same frame), go ahead and reset + // the dirty related properties and last sent time to prevent duplicate entries from + // being sent (i.e. CreateObjectMessage will contain the changes so we don't need to + // send a proceeding NetworkVariableDeltaMessage). if (IsDirty() && CanSend() && m_NetworkBehaviour.HasAuthority) { UpdateLastSentTime(); From 4d56f50472c67dadf54db5a793e8d9ede155157c Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 21 Jan 2026 12:33:33 -0600 Subject: [PATCH 06/10] test Updating NetworkListTests to include an owner write test scenario that validates initial spawn, late join with spawn, and then changes to the owner write NetworkLists. --- .../NetworkVariable/NetworkListTests.cs | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs index 15c737fbc3..2e7e9b6532 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs @@ -27,6 +27,12 @@ public NetworkListTests(HostOrServer host) : base(host) { } private ulong m_TestObjectId; + protected override IEnumerator OnSetup() + { + IsOwnerWriteTest = false; + return base.OnSetup(); + } + protected override void OnServerAndClientsCreated() { m_ListObjectPrefab = CreateNetworkObjectPrefab("ListObject"); @@ -285,6 +291,136 @@ private int[] Shuffle(List list) // This will do a shuffle of the list return list.OrderBy(_ => rng.Next()).ToArray(); } + + private List m_SpawnedObjects = new List(); + internal const int SpawnCount = 10; + internal bool IsOwnerWriteTest; + internal NetworkManager LateJoinedClient; + + protected override void OnNewClientCreated(NetworkManager networkManager) + { + if (IsOwnerWriteTest) + { + LateJoinedClient = networkManager; + } + else + { + LateJoinedClient = null; + } + base.OnNewClientCreated(networkManager); + } + + [UnityTest] + public IEnumerator OwnerWriteTests() + { + IsOwnerWriteTest = true; + var authorityBetworkManager = GetAuthorityNetworkManager(); + m_SpawnedObjects.Clear(); + m_ExpectedValues.Clear(); + // Set our initial expected values as 0 - 9 + for (int i = 0; i < SpawnCount; i++) + { + m_ExpectedValues.Add(i); + } + + // Each spawned instance will be owned by each NetworkManager instance in order + // to validate owner write NetworkLists. + foreach (var networkManager in m_NetworkManagers) + { + m_SpawnedObjects.Add(SpawnObject(m_ListObjectPrefab, networkManager).GetComponent()); + } + + // Verify all NetworkManager instances spawned the objects + yield return WaitForSpawnedOnAllOrTimeOut(m_SpawnedObjects); + AssertOnTimeout("Not all instances were spawned on all clients!"); + + // Verify all spawned object instances have the expected owner write NetworkList values + yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData); + AssertOnTimeout("Detected invalid count or value on one of the spawned instances!"); + + // Late join a client + yield return CreateAndStartNewClient(); + + // Spawn an instance with the new client being the owner + m_SpawnedObjects.Add(SpawnObject(m_ListObjectPrefab, LateJoinedClient).GetComponent()); + + // Verify all NetworkManager instances spawned the objects + yield return WaitForSpawnedOnAllOrTimeOut(m_SpawnedObjects); + AssertOnTimeout("Not all instances were spawned on all clients!"); + + // Verify all spawned object instances have the expected owner write NetworkList values + yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData); + AssertOnTimeout("Detected invalid count or value on one of the spawned instances!"); + + // Now have all of the clients update their list values to randomly assigned values + // in order to verify changes to owner write NetworkLists are synchronized properly. + m_ExpectedValues.Clear(); + for (int i = 0; i < SpawnCount; i++) + { + m_ExpectedValues.Add(Random.Range(10, 100)); + } + UpdateOwnerWriteValues(); + + // Verify all spawned object instances have the expected owner write NetworkList values + yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData); + AssertOnTimeout("Detected invalid count or value on one of the spawned instances!"); + } + + private void UpdateOwnerWriteValues() + { + foreach (var spawnedObject in m_SpawnedObjects) + { + var owningNetworkManager = m_NetworkManagers.Where((c) => c.LocalClientId == spawnedObject.OwnerClientId).First(); + var networkObjectId = spawnedObject.NetworkObjectId; + var listComponent = owningNetworkManager.SpawnManager.SpawnedObjects[networkObjectId].GetComponent(); + for (int i = 0; i < SpawnCount; i++) + { + listComponent.OwnerWriteList[i] = m_ExpectedValues[i]; + } + } + } + + private bool OnVerifyOwnerWriteData(StringBuilder errorLog) + { + foreach (var spawnedObject in m_SpawnedObjects) + { + var networkObjectId = spawnedObject.NetworkObjectId; + foreach (var networkManager in m_NetworkManagers) + { + if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] Does not have an instance of spawned object NetworkObjectId: {networkObjectId}"); + return false; + } + var listComponent = networkManager.SpawnManager.SpawnedObjects[networkObjectId].GetComponent(); + + if (listComponent == null) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] List component was not found"); + return false; + } + + if (listComponent.OwnerWriteList.Count != SpawnCount) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] List component has the incorrect number of items. Expected: {SpawnCount}, Have: {listComponent.TheList.Count}"); + return false; + } + + for (int i = 0; i < SpawnCount; i++) + { + var actual = listComponent.OwnerWriteList[i]; + var expected = m_ExpectedValues[i]; + if (expected != actual) + { + errorLog.Append($"[Client-{networkManager.LocalClientId}] Incorrect value at index {i}, expected: {expected}, actual: {actual}"); + return false; + } + } + } + } + + return true; + } } internal class NetworkListTest : NetworkBehaviour @@ -292,6 +428,7 @@ internal class NetworkListTest : NetworkBehaviour public readonly NetworkList TheList = new(); public readonly NetworkList TheStructList = new(); public readonly NetworkList TheLargeList = new(); + public readonly NetworkList OwnerWriteList = new NetworkList(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner); private void ListChanged(NetworkListEvent e) { @@ -309,6 +446,18 @@ public override void OnDestroy() base.OnDestroy(); } + public override void OnNetworkSpawn() + { + if (IsOwner) + { + for (int i = 0; i < NetworkListTests.SpawnCount; i++) + { + OwnerWriteList.Add(i); + } + } + base.OnNetworkSpawn(); + } + public bool ListDelegateTriggered; } From 412639a113fbdaf065d2041ed31b5378a5753018 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 21 Jan 2026 12:40:30 -0600 Subject: [PATCH 07/10] style - PVP Removing trailing space from comment. --- .../Tests/Runtime/NetworkVariable/NetworkListTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs index 2e7e9b6532..9793d64f53 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs @@ -338,7 +338,7 @@ public IEnumerator OwnerWriteTests() yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData); AssertOnTimeout("Detected invalid count or value on one of the spawned instances!"); - // Late join a client + // Late join a client yield return CreateAndStartNewClient(); // Spawn an instance with the new client being the owner From 819a6034cf24c87a5dfd18c7a028ca603adf89df Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 22 Jan 2026 17:13:51 -0600 Subject: [PATCH 08/10] update & test This resolves an issue discovered after peer review where using just `HasAuthority` as part of the check within `NetworkList.OnSpawned` while using a distributed authority topology and spawning with ownership (i.e. Client-A spawns with ownership where Client-B is the owner) would result in any changes made on the authority side during spawn would not be synchronized. The solution was to add `NetworkObject.IsSpawnAuthority` (internal) that is only set on the spawn authority side (i.e. the side that generates the `CreateObjectMessage`). --- .../Runtime/Core/NetworkObject.cs | 11 ++++ .../Collections/NetworkList.cs | 2 +- .../Runtime/Spawning/NetworkSpawnManager.cs | 3 + .../NetworkVariable/NetworkListTests.cs | 59 +++++++++++++------ 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index cbcd40ed4d..f80c98b425 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -76,6 +76,16 @@ public uint PrefabIdHash /// public List NetworkTransforms { get; private set; } + /// + /// Set to true if this instance is the original/first instance created and spawned which + /// means the was generated from this instance and sent + /// to all other clients. + /// + /// Client-Server: This will be true for all instances on the server or host. + /// Distributed Authority: This will be true on the client that created the first instance + /// and spawned it (even if spawning with ownership being assigned to a different client). + /// + internal bool IsSpawnAuthority; #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D /// @@ -2008,6 +2018,7 @@ internal void ResetOnDespawn() { // Always clear out the observers list when despawned Observers.Clear(); + IsSpawnAuthority = false; IsSpawned = false; DeferredDespawnTick = 0; m_LatestParent = null; diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index 2b2410dc67..3b2117bf11 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -71,7 +71,7 @@ internal override void OnSpawned() // the dirty related properties and last sent time to prevent duplicate entries from // being sent (i.e. CreateObjectMessage will contain the changes so we don't need to // send a proceeding NetworkVariableDeltaMessage). - if (IsDirty() && CanSend() && m_NetworkBehaviour.HasAuthority) + if (IsDirty() && CanSend() && m_NetworkObject.IsSpawnAuthority) { UpdateLastSentTime(); ResetDirty(); diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index c157c68cea..3efd90779d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1054,6 +1054,9 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n Debug.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!"); } } + + networkObject.IsSpawnAuthority = true; + // Invoke NetworkBehaviour.OnPreSpawn methods networkObject.NetworkManagerOwner = NetworkManager; networkObject.InvokeBehaviourNetworkPreSpawn(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs index 9793d64f53..da85889bc3 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs @@ -293,9 +293,10 @@ private int[] Shuffle(List list) } private List m_SpawnedObjects = new List(); - internal const int SpawnCount = 10; + internal const int ValueCount = 10; internal bool IsOwnerWriteTest; internal NetworkManager LateJoinedClient; + internal static List OwnerWriteExpectedValues = new List(); protected override void OnNewClientCreated(NetworkManager networkManager) { @@ -314,13 +315,13 @@ protected override void OnNewClientCreated(NetworkManager networkManager) public IEnumerator OwnerWriteTests() { IsOwnerWriteTest = true; - var authorityBetworkManager = GetAuthorityNetworkManager(); + var authorityNetworkManager = GetAuthorityNetworkManager(); m_SpawnedObjects.Clear(); - m_ExpectedValues.Clear(); + OwnerWriteExpectedValues.Clear(); // Set our initial expected values as 0 - 9 - for (int i = 0; i < SpawnCount; i++) + for (int i = 0; i < ValueCount; i++) { - m_ExpectedValues.Add(i); + OwnerWriteExpectedValues.Add(i); } // Each spawned instance will be owned by each NetworkManager instance in order @@ -354,16 +355,40 @@ public IEnumerator OwnerWriteTests() // Now have all of the clients update their list values to randomly assigned values // in order to verify changes to owner write NetworkLists are synchronized properly. - m_ExpectedValues.Clear(); - for (int i = 0; i < SpawnCount; i++) + OwnerWriteExpectedValues.Clear(); + for (int i = 0; i < ValueCount; i++) { - m_ExpectedValues.Add(Random.Range(10, 100)); + OwnerWriteExpectedValues.Add(Random.Range(10, 100)); } UpdateOwnerWriteValues(); // Verify all spawned object instances have the expected owner write NetworkList values yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData); AssertOnTimeout("Detected invalid count or value on one of the spawned instances!"); + + // Verifies that spawning with ownership in distributed authority mode work properly. + // Where: + // Client-A spawns with ownership assigned to Client-B + // Client-B applies values at spawn. + // All clients then should be updated with those new values applied. + if (m_DistributedAuthority) + { + var prefabNetworkObject = m_ListObjectPrefab.GetComponent(); + foreach (var networkManager in m_NetworkManagers) + { + var instance = Object.Instantiate(m_ListObjectPrefab).GetComponent(); + SpawnInstanceWithOwnership(instance, authorityNetworkManager, networkManager.LocalClientId); + m_SpawnedObjects.Add(instance); + } + + // Verify all NetworkManager instances spawned the objects + yield return WaitForSpawnedOnAllOrTimeOut(m_SpawnedObjects); + AssertOnTimeout("Not all instances were spawned on all clients!"); + + // Verify all spawned object instances have the expected owner write NetworkList values + yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData); + AssertOnTimeout("Detected invalid count or value on one of the spawned instances!"); + } } private void UpdateOwnerWriteValues() @@ -373,9 +398,9 @@ private void UpdateOwnerWriteValues() var owningNetworkManager = m_NetworkManagers.Where((c) => c.LocalClientId == spawnedObject.OwnerClientId).First(); var networkObjectId = spawnedObject.NetworkObjectId; var listComponent = owningNetworkManager.SpawnManager.SpawnedObjects[networkObjectId].GetComponent(); - for (int i = 0; i < SpawnCount; i++) + for (int i = 0; i < ValueCount; i++) { - listComponent.OwnerWriteList[i] = m_ExpectedValues[i]; + listComponent.OwnerWriteList[i] = OwnerWriteExpectedValues[i]; } } } @@ -400,16 +425,16 @@ private bool OnVerifyOwnerWriteData(StringBuilder errorLog) return false; } - if (listComponent.OwnerWriteList.Count != SpawnCount) + if (listComponent.OwnerWriteList.Count != ValueCount) { - errorLog.Append($"[Client-{networkManager.LocalClientId}] List component has the incorrect number of items. Expected: {SpawnCount}, Have: {listComponent.TheList.Count}"); + errorLog.Append($"[Client-{networkManager.LocalClientId}] List component has the incorrect number of items. Expected: {ValueCount}, Have: {listComponent.TheList.Count}"); return false; } - for (int i = 0; i < SpawnCount; i++) + for (int i = 0; i < ValueCount; i++) { var actual = listComponent.OwnerWriteList[i]; - var expected = m_ExpectedValues[i]; + var expected = OwnerWriteExpectedValues[i]; if (expected != actual) { errorLog.Append($"[Client-{networkManager.LocalClientId}] Incorrect value at index {i}, expected: {expected}, actual: {actual}"); @@ -450,9 +475,9 @@ public override void OnNetworkSpawn() { if (IsOwner) { - for (int i = 0; i < NetworkListTests.SpawnCount; i++) + for (int i = 0; i < NetworkListTests.ValueCount; i++) { - OwnerWriteList.Add(i); + OwnerWriteList.Add(NetworkListTests.OwnerWriteExpectedValues[i]); } } base.OnNetworkSpawn(); @@ -474,8 +499,6 @@ internal class NetworkListTestPredicate : ConditionalPredicateBase private readonly NetworkListTest m_NonAuthorityInstance; - private string m_TestStageFailedMessage; - /// /// Determines if the condition has been reached for the current NetworkListTestState /// From 9fc4f3edd86a4101fe7bb98c8e9365cac0ac1a3f Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 22 Jan 2026 17:29:29 -0600 Subject: [PATCH 09/10] style - PVP Removing single trailing whitespace from comment. --- com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index f80c98b425..45d4b3311e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -80,7 +80,7 @@ public uint PrefabIdHash /// Set to true if this instance is the original/first instance created and spawned which /// means the was generated from this instance and sent /// to all other clients. - /// + /// /// Client-Server: This will be true for all instances on the server or host. /// Distributed Authority: This will be true on the client that created the first instance /// and spawned it (even if spawning with ownership being assigned to a different client). From 4b90d46c58885a52c0d0f09da36199771639422b Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 22 Jan 2026 18:52:36 -0600 Subject: [PATCH 10/10] test - fix Only populate the owner write NetworkList values when running the owner write tests. --- .../Tests/Runtime/NetworkVariable/NetworkListTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs index da85889bc3..5f3be93f57 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkListTests.cs @@ -294,7 +294,7 @@ private int[] Shuffle(List list) private List m_SpawnedObjects = new List(); internal const int ValueCount = 10; - internal bool IsOwnerWriteTest; + internal static bool IsOwnerWriteTest; internal NetworkManager LateJoinedClient; internal static List OwnerWriteExpectedValues = new List(); @@ -473,7 +473,7 @@ public override void OnDestroy() public override void OnNetworkSpawn() { - if (IsOwner) + if (NetworkListTests.IsOwnerWriteTest && IsOwner) { for (int i = 0; i < NetworkListTests.ValueCount; i++) {