From 88c68314083a009e832fadb8b2047716f4f262f4 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Mon, 23 Mar 2026 16:16:33 +0100 Subject: [PATCH 1/7] Start remove exception and comment cleanup --- .../Runtime/Spawning/NetworkSpawnManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index bf4eacbc1b..29f8881af8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -526,9 +526,14 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool } else if (!isAuthorized) { - throw new NotServerException("Only the server can change ownership"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Only the server can change ownership! (ignoring)"); + return; + } } + //Should this go at the beginning of the function? if (!networkObject.IsSpawned) { throw new SpawnStateException("Object is not spawned"); From c3ce52e7ccdb0921c73ee598ee0553cafe66ff17 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Tue, 7 Apr 2026 18:07:03 +0200 Subject: [PATCH 2/7] Replace Debug by NetworkLog + remove exceptions --- .../Runtime/Spawning/NetworkSpawnManager.cs | 194 +++++++++++++----- 1 file changed, 146 insertions(+), 48 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 29f8881af8..e1c346122d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -211,8 +211,10 @@ internal bool RemoveObjectFromShowingTo(NetworkObject networkObject, ulong clien // probably overkill, but deals with multiple entries while (ObjectsToShowToClient[clientId].Contains(networkObject)) { - Debug.LogWarning( - "Object was shown and hidden from the same client in the same Network frame. As a result, the client will _not_ receive a NetworkSpawn"); + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"Object was shown and hidden from the same client in the same Network frame. As a result, the client will _not_ receive a NetworkSpawn"); + } ObjectsToShowToClient[clientId].Remove(networkObject); ret = true; } @@ -277,7 +279,11 @@ internal void UpdateOwnershipTable(NetworkObject networkObject, ulong newOwner, else { // Really, as long as UpdateOwnershipTable is invoked when ownership is gained or lost this should never happen - throw new Exception($"Client-ID {previousOwner} had a partial {nameof(m_ObjectToOwnershipTable)} entry! Potentially corrupted {nameof(OwnershipToObjectsTable)}?"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Client-ID {previousOwner} had a partial {nameof(m_ObjectToOwnershipTable)} entry! Potentially corrupted {nameof(OwnershipToObjectsTable)}?"); + } + return; } } @@ -375,7 +381,11 @@ public NetworkObject GetPlayerNetworkObject(ulong clientId) { if (!NetworkManager.IsServer && NetworkManager.LocalClientId != clientId) { - throw new NotServerException("Only the server can find player objects from other clients."); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"{clientId} Only the server can find player objects from other clients."); + } + return null; } if (TryGetNetworkClient(clientId, out NetworkClient networkClient)) { @@ -389,7 +399,6 @@ public NetworkObject GetPlayerNetworkObject(ulong clientId) return m_PlayerObjectsTable[clientId].First(); } } - return null; } @@ -432,7 +441,10 @@ internal void RemoveOwnership(NetworkObject networkObject) { if (NetworkManager.DistributedAuthorityMode && !NetworkManager.ShutdownInProgress) { - Debug.LogError($"Removing ownership is invalid in Distributed Authority Mode. Use {nameof(ChangeOwnership)} instead."); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Removing ownership is invalid in Distributed Authority Mode. Use {nameof(ChangeOwnership)} instead."); + } return; } ChangeOwnership(networkObject, NetworkManager.ServerClientId, true); @@ -443,11 +455,20 @@ internal void RemoveOwnership(NetworkObject networkObject) internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool isAuthorized, bool isRequestApproval = false) { + if (!networkObject.IsSpawned) + { + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{networkObject.name}] Cannot change ownership while not spawned."); + } + return; + } + if (clientId == networkObject.OwnerClientId) { if (NetworkManager.LogLevel <= LogLevel.Developer) { - Debug.LogWarning($"[{nameof(NetworkSpawnManager)}][{nameof(ChangeOwnership)}] Attempting to change ownership to Client-{clientId} when the owner is already {networkObject.OwnerClientId}! (Ignoring)"); + NetworkLog.LogWarning($"[{nameof(NetworkSpawnManager)}][{nameof(ChangeOwnership)}] Attempting to change ownership to Client-{clientId} when the owner is already {networkObject.OwnerClientId}! (Ignoring)"); } return; } @@ -528,20 +549,14 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool { if (NetworkManager.LogLevel <= LogLevel.Error) { - NetworkLog.LogError($"Only the server can change ownership! (ignoring)"); - return; + NetworkLog.LogError("Only the server can change ownership! (ignoring)"); } - } - - //Should this go at the beginning of the function? - if (!networkObject.IsSpawned) - { - throw new SpawnStateException("Object is not spawned"); + return; } if (!networkObject.Observers.Contains(clientId)) { - if (NetworkManager.LogLevel == LogLevel.Developer) + if (NetworkManager.LogLevel <= LogLevel.Developer) { NetworkLog.LogWarningServer($"[Invalid Owner] Cannot send Ownership change as client-{clientId} cannot see {networkObject.name}! Use {nameof(NetworkObject.NetworkShow)} first."); } @@ -742,7 +757,10 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne { if (networkPrefab == null) { - Debug.LogError(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.NetworkPrefabNull]); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.NetworkPrefabNull]); + } return null; } @@ -750,20 +768,29 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne // We only need to check for authority when running in client-server mode if (!NetworkManager.IsServer && !NetworkManager.DistributedAuthorityMode) { - Debug.LogError(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.NotAuthority]); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.NotAuthority]); + } return null; } if (NetworkManager.ShutdownInProgress) { - Debug.LogWarning(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.InvokedWhenShuttingDown]); + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.InvokedWhenShuttingDown]); + } return null; } // Verify it is actually a valid prefab if (!NetworkManager.NetworkConfig.Prefabs.Contains(networkPrefab.gameObject)) { - Debug.LogError(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.NotRegisteredNetworkPrefab]); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.NotRegisteredNetworkPrefab]); + } return null; } @@ -792,7 +819,10 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ if (networkObject == null) { - Debug.LogError($"Failed to instantiate and spawn {networkPrefab.name}!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Failed to instantiate and spawn {networkPrefab.name}!"); + } return null; } @@ -1048,7 +1078,10 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n { if (networkObject.IsSpawned) { - Debug.LogError($"{networkObject.name} is already spawned!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"{networkObject.name} is already spawned!"); + } return; } @@ -1057,7 +1090,10 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n var networkObjectChildren = networkObject.GetComponentsInChildren(); if (networkObjectChildren.Length > 1) { - Debug.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!"); + } } } @@ -1100,7 +1136,10 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n // Itentionally checking as opposed to just assigning in order to generate notification. if (!networkObject.Observers.Contains(ownerClientId)) { - Debug.LogError($"Client-{ownerClientId} is the owner of {networkObject.name} but is not an observer! Adding owner, but there is a bug in observer synchronization!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"Client-{ownerClientId} is the owner of {networkObject.name} but is not an observer! Adding owner, but there is a bug in observer synchronization!"); + } networkObject.AddObserver(ownerClientId); } } @@ -1126,7 +1165,10 @@ internal void NonAuthorityLocalSpawn([NotNull] NetworkObject networkObject, in N { if (networkObject.IsSpawned) { - Debug.LogError($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!"); + } return; } @@ -1144,12 +1186,18 @@ internal void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong { if (networkObject.NetworkManagerOwner == null) { - Debug.LogError("NetworkManagerOwner should not be null!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"{networkObject.name}'s NetworkManagerOwner should not be null!"); + } } if (SpawnedObjects.ContainsKey(networkId)) { - Debug.LogWarning($"[{NetworkManager.name}] Trying to spawn {networkObject.name} with a {nameof(NetworkObject.NetworkObjectId)} of {networkId} but it is already in the spawned list!"); + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"[{NetworkManager.name}] Trying to spawn {networkObject.name} with a {nameof(NetworkObject.NetworkObjectId)} of {networkId} but it is already in the spawned list!"); + } return; } @@ -1316,7 +1364,11 @@ internal void SendSpawnCallForObserverUpdate(ulong[] newObservers, NetworkObject { if (!NetworkManager.DistributedAuthorityMode) { - throw new Exception("[SendSpawnCallForObserverUpdate] Invoking a distributed authority only method when distributed authority is not enabled!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[SendSpawnCallForObserverUpdate] Invoking a distributed authority only method on {networkObject.name} when distributed authority is not enabled!"); + } + return; } var message = new CreateObjectMessage @@ -1357,7 +1409,10 @@ internal void DespawnObject(NetworkObject networkObject, bool destroyObject = fa { if (!NetworkManager.IsServer && !NetworkManager.DistributedAuthorityMode) { - NetworkLog.LogErrorServer("Only server can despawn objects"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer("Only server can despawn objects"); + } return; } @@ -1365,7 +1420,10 @@ internal void DespawnObject(NetworkObject networkObject, bool destroyObject = fa { if (!NetworkManager.DAHost || NetworkManager.DAHost && !authorityOverride) { - NetworkLog.LogErrorServer($"In distributed authority mode, only the owner of the NetworkObject can despawn it! Local Client is ({NetworkManager.LocalClientId}) while the owner is ({networkObject.OwnerClientId})"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"In distributed authority mode, only the owner of the NetworkObject can despawn it! Local Client is ({NetworkManager.LocalClientId}) while the owner is ({networkObject.OwnerClientId})"); + } return; } } @@ -1541,7 +1599,10 @@ internal void OnDespawnNonAuthorityObject([NotNull] NetworkObject networkObject, { if (networkObject.HasAuthority) { - NetworkLog.LogError($"OnDespawnNonAuthorityObject called on object {networkObject.NetworkObjectId} when is current client {NetworkManager.LocalClientId} has authority on this object."); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[OnDespawnNonAuthorityObject] called on object {networkObject.NetworkObjectId} when its current client {NetworkManager.LocalClientId} has authority on this object."); + } } if (networkObject.IsSceneObject == false) @@ -1569,7 +1630,10 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec // We have to do this check first as subsequent checks assume we can access NetworkObjectId. if (!networkObject) { - NetworkLog.LogWarning("Trying to destroy network object but it is null"); + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning("Trying to destroy network object but it is null"); + } return; } @@ -1578,7 +1642,10 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec { if (!NetworkManager.ShutdownInProgress && !NetworkManager.SceneManager.IsSceneEventInProgress()) { - NetworkLog.LogWarning($"Trying to destroy object {networkObject.NetworkObjectId} but it doesn't seem to exist anymore!"); + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"Trying to destroy object {networkObject.NetworkObjectId} but it doesn't seem to exist anymore!"); + } } return; } @@ -1592,7 +1659,10 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec { if (destroyGameObject && networkObject.IsSceneObject == true && !NetworkManager.SceneManager.IsSceneUnloading(networkObject)) { - NetworkLog.LogWarning("Destroying in-scene network objects can lead to unexpected behavior. It is recommended to use NetworkObject.Despawn(false) instead."); + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning("Destroying in-scene network objects can lead to unexpected behavior. It is recommended to use NetworkObject.Despawn(false) instead."); + } } // Get all child NetworkObjects @@ -1776,9 +1846,9 @@ internal void HandleNetworkObjectShow(bool forceSend = false) return; } - // In distributed authority mode, we send a single message that is broadcasted to all clients - // that will be shown the object (i.e. 1 message to service that then broadcasts that to the - // targeted clients). When using a DAHost, we skip this and send like we do in client-server + // In distributed authority mode, we send a single message to the service, + // which then broadcasts it to all clients that should see the object. + // When using a DAHost, we skip this and send using the client-server approach. if (isDistributedAuthorityClient) { var behaviourUpdater = NetworkManager.BehaviourUpdater; @@ -1949,6 +2019,10 @@ internal void DistributeNetworkObjects(ulong clientId) { if (!NetworkManager.DistributedAuthorityMode) { + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError("[DistributeNetworkObjects] called while not in Distributed Authority Mode!"); + } return; } @@ -2015,7 +2089,10 @@ internal void DistributeNetworkObjects(ulong clientId) var offsetCount = Mathf.Max((int)Math.Round((float)(ownerList.Value.Count / objPerClient)), 1); if (EnableDistributeLogging) { - Debug.Log($"[{objPerClient} of {totalObjectsToDistribute}][Client-{ownerList.Key}] Count: {ownerList.Value.Count} | ObjPerClient: {objPerClient} | maxD: {maxDistributeCount} | Offset: {offsetCount}"); + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"[{objPerClient} of {totalObjectsToDistribute}][Client-{ownerList.Key}] Count: {ownerList.Value.Count} | ObjPerClient: {objPerClient} | maxD: {maxDistributeCount} | Offset: {offsetCount}"); + } } for (int i = 0; i < ownerList.Value.Count; i++) @@ -2035,7 +2112,10 @@ internal void DistributeNetworkObjects(ulong clientId) } if (!child.IsOwnershipDistributable || !child.IsOwnershipTransferable) { - NetworkLog.LogWarning($"Sibling {child.name} of root parent {ownerList.Value[i].name} is neither transferable or distributable! Object distribution skipped and could lead to a potentially un-owned or owner-mismatched {nameof(NetworkObject)}!"); + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"Sibling {child.name} of root parent {ownerList.Value[i].name} is neither transferable or distributable! Object distribution skipped and could lead to a potentially un-owned or owner-mismatched {nameof(NetworkObject)}!"); + } continue; } // Transfer ownership of all distributable =or= transferable children with the same owner to the same client to preserve the sibling ownership tree. @@ -2046,7 +2126,10 @@ internal void DistributeNetworkObjects(ulong clientId) ChangeOwnership(ownerList.Value[i], clientId, true); if (EnableDistributeLogging) { - Debug.Log($"[Client-{ownerList.Key}][NetworkObjectId-{ownerList.Value[i].NetworkObjectId} Distributed to Client-{clientId}"); + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"[Client-{ownerList.Key}][NetworkObjectId-{ownerList.Value[i].NetworkObjectId} Distributed to Client-{clientId}"); + } } distributed++; } @@ -2077,7 +2160,10 @@ internal void DistributeNetworkObjects(ulong clientId) builder.AppendLine($"[Client-{ownerList.Key}] Count: {ownerList.Value.Count}"); } } - Debug.Log(builder.ToString()); + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo(builder.ToString()); + } } } @@ -2201,19 +2287,25 @@ internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId) { if (NetworkManager == null || NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer) { - Debug.LogWarning($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!"); + NetworkLog.LogWarning($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!"); return; } if (!NetworkManager.DistributedAuthorityMode) { - Debug.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); + } return; } if (NetworkManager.LocalClient.IsSessionOwner) { - Debug.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked on a non-session owner client!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked on a non-session owner client!"); + } return; } var localClientId = NetworkManager.LocalClient.ClientId; @@ -2227,7 +2319,7 @@ internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId) if (NetworkManager.LogLevel <= LogLevel.Developer) { // Track if there is some other location where the client is being added to the observers list when the object is hidden from the session owner - Debug.LogWarning($"[{networkObject.name}] Has new client as an observer but it is hidden from the session owner!"); + NetworkLog.LogWarning($"[{networkObject.name}] Has new client as an observer but it is hidden from the session owner!"); } // For now, remove the client (impossible for the new client to have an instance since the session owner doesn't) to make sure newly added // code to handle this edge case works. @@ -2242,19 +2334,25 @@ internal void SynchronizeObjectsToNewlyJoinedClient(ulong newClientId) { if (NetworkManager == null || NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer) { - Debug.LogWarning($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!"); + NetworkLog.LogWarning($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!"); return; } if (!NetworkManager.DistributedAuthorityMode) { - Debug.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); + } return; } if (NetworkManager.NetworkConfig.EnableSceneManagement) { - Debug.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when scene management is disabled!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when scene management is disabled!"); + } return; } From 74dd16efc4dd05951b401c1508a0b3755da24044 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Tue, 7 Apr 2026 18:14:26 +0200 Subject: [PATCH 3/7] Move return call out of the log level check --- .../Runtime/Spawning/NetworkSpawnManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index e1c346122d..ff8b6d0517 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -75,11 +75,11 @@ private void AddPlayerObject(NetworkObject playerObject) { if (!playerObject.IsPlayerObject) { - if (NetworkManager.LogLevel == LogLevel.Normal) + if (NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogError($"Attempting to register a {nameof(NetworkObject)} as a player object but {nameof(NetworkObject.IsPlayerObject)} is not set!"); - return; } + return; } var cmbService = NetworkManager.CMBServiceConnection; @@ -148,11 +148,11 @@ private void RemovePlayerObject(NetworkObject playerObject, bool destroyingObjec { if (!playerObject.IsPlayerObject) { - if (NetworkManager.LogLevel == LogLevel.Normal) + if (NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogError($"Attempting to deregister a {nameof(NetworkObject)} as a player object but {nameof(NetworkObject.IsPlayerObject)} is not set!"); - return; } + return; } playerObject.IsPlayerObject = false; m_PlayerObjects.Remove(playerObject); From d4bef4334f94faa24b8790babb80c9f3410de053 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Tue, 7 Apr 2026 21:03:27 +0200 Subject: [PATCH 4/7] Fix test + remove not reachable code --- .../Runtime/Spawning/NetworkSpawnManager.cs | 20 ++++--------------- .../Tests/Runtime/NetworkSpawnManagerTests.cs | 7 +++---- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index ff8b6d0517..7fa85ca269 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -1856,6 +1856,8 @@ internal void HandleNetworkObjectShow(bool forceSend = false) { if (entry.Key != null && entry.Key.IsSpawned) { + // Try catch due to ensure that if anything throws we keep processing the list. + // This can throw if the GameObject was destroyed on this frame. try { // Always push the most recent deltas when showing a NetworkObject @@ -1885,6 +1887,8 @@ internal void HandleNetworkObjectShow(bool forceSend = false) { if (networkObject != null && networkObject.IsSpawned) { + // Try catch due to ensure that if anything throws we keep processing the list. + // This can throw if the GameObject was destroyed on this frame. try { if (forceSend) @@ -2019,10 +2023,6 @@ internal void DistributeNetworkObjects(ulong clientId) { if (!NetworkManager.DistributedAuthorityMode) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogError("[DistributeNetworkObjects] called while not in Distributed Authority Mode!"); - } return; } @@ -2285,12 +2285,6 @@ internal void NotifyNetworkObjectsSynchronized() /// internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId) { - if (NetworkManager == null || NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogWarning($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!"); - return; - } - if (!NetworkManager.DistributedAuthorityMode) { if (NetworkManager.LogLevel <= LogLevel.Error) @@ -2332,12 +2326,6 @@ internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId) internal void SynchronizeObjectsToNewlyJoinedClient(ulong newClientId) { - if (NetworkManager == null || NetworkManager.ShutdownInProgress && NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogWarning($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} invoked while shutdown is in progress!"); - return; - } - if (!NetworkManager.DistributedAuthorityMode) { if (NetworkManager.LogLevel <= LogLevel.Error) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs index 3039ea298c..93f3492456 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs @@ -100,10 +100,9 @@ public void TestClientCantAccessOtherPlayer() } // client can't access other player - Assert.Throws(() => - { - m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(otherClientSideClientId); - }); + string expectedLog = $"[Netcode-Server Sender=0] {otherClientSideClientId} Only the server can find player objects from other clients."; + LogAssert.Expect(UnityEngine.LogType.Error, expectedLog); + m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(otherClientSideClientId); } [Test] From 1e64764321012a45e912985e52b68fde64c7d5ea Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 8 Apr 2026 16:39:21 +0200 Subject: [PATCH 5/7] Fix log level + revert internal log and EnableDistributeLogging --- .../Runtime/Spawning/NetworkSpawnManager.cs | 41 +++++-------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 7fa85ca269..928133e5f0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -211,7 +211,7 @@ internal bool RemoveObjectFromShowingTo(NetworkObject networkObject, ulong clien // probably overkill, but deals with multiple entries while (ObjectsToShowToClient[clientId].Contains(networkObject)) { - if (NetworkManager.LogLevel <= LogLevel.Developer) + if (NetworkManager.LogLevel > LogLevel.Normal) { NetworkLog.LogWarning($"Object was shown and hidden from the same client in the same Network frame. As a result, the client will _not_ receive a NetworkSpawn"); } @@ -777,7 +777,7 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne if (NetworkManager.ShutdownInProgress) { - if (NetworkManager.LogLevel <= LogLevel.Developer) + if (NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogWarning(InstantiateAndSpawnErrors[InstantiateAndSpawnErrorTypes.InvokedWhenShuttingDown]); } @@ -2089,10 +2089,7 @@ internal void DistributeNetworkObjects(ulong clientId) var offsetCount = Mathf.Max((int)Math.Round((float)(ownerList.Value.Count / objPerClient)), 1); if (EnableDistributeLogging) { - if (NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"[{objPerClient} of {totalObjectsToDistribute}][Client-{ownerList.Key}] Count: {ownerList.Value.Count} | ObjPerClient: {objPerClient} | maxD: {maxDistributeCount} | Offset: {offsetCount}"); - } + Debug.Log($"[{objPerClient} of {totalObjectsToDistribute}][Client-{ownerList.Key}] Count: {ownerList.Value.Count} | ObjPerClient: {objPerClient} | maxD: {maxDistributeCount} | Offset: {offsetCount}"); } for (int i = 0; i < ownerList.Value.Count; i++) @@ -2112,7 +2109,7 @@ internal void DistributeNetworkObjects(ulong clientId) } if (!child.IsOwnershipDistributable || !child.IsOwnershipTransferable) { - if (NetworkManager.LogLevel <= LogLevel.Developer) + if (NetworkManager.LogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"Sibling {child.name} of root parent {ownerList.Value[i].name} is neither transferable or distributable! Object distribution skipped and could lead to a potentially un-owned or owner-mismatched {nameof(NetworkObject)}!"); } @@ -2126,10 +2123,7 @@ internal void DistributeNetworkObjects(ulong clientId) ChangeOwnership(ownerList.Value[i], clientId, true); if (EnableDistributeLogging) { - if (NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"[Client-{ownerList.Key}][NetworkObjectId-{ownerList.Value[i].NetworkObjectId} Distributed to Client-{clientId}"); - } + Debug.Log($"[Client-{ownerList.Key}][NetworkObjectId-{ownerList.Value[i].NetworkObjectId} Distributed to Client-{clientId}"); } distributed++; } @@ -2160,10 +2154,7 @@ internal void DistributeNetworkObjects(ulong clientId) builder.AppendLine($"[Client-{ownerList.Key}] Count: {ownerList.Value.Count}"); } } - if (NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo(builder.ToString()); - } + Debug.Log(builder.ToString()); } } @@ -2287,19 +2278,13 @@ internal void ShowHiddenObjectsToNewlyJoinedClient(ulong newClientId) { if (!NetworkManager.DistributedAuthorityMode) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); - } + Debug.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); return; } if (NetworkManager.LocalClient.IsSessionOwner) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked on a non-session owner client!"); - } + Debug.LogError($"[Internal Error] {nameof(ShowHiddenObjectsToNewlyJoinedClient)} should only be invoked on a non-session owner client!"); return; } var localClientId = NetworkManager.LocalClient.ClientId; @@ -2328,19 +2313,13 @@ internal void SynchronizeObjectsToNewlyJoinedClient(ulong newClientId) { if (!NetworkManager.DistributedAuthorityMode) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); - } + Debug.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when using a distributed authority network topology!"); return; } if (NetworkManager.NetworkConfig.EnableSceneManagement) { - if (NetworkManager.LogLevel <= LogLevel.Error) - { - NetworkLog.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when scene management is disabled!"); - } + Debug.LogError($"[Internal Error] {nameof(SynchronizeObjectsToNewlyJoinedClient)} should only be invoked when scene management is disabled!"); return; } From 960000e82079515f105f349378bb04ac1d203b47 Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Wed, 8 Apr 2026 17:33:36 +0200 Subject: [PATCH 6/7] Update test to expect Log error instead of exception --- .../Tests/Runtime/NetworkSpawnManagerTests.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs index 93f3492456..a0d51696bb 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkSpawnManagerTests.cs @@ -61,10 +61,9 @@ public void TestClientCantAccessServerPlayer() return; } // client can't access server player - Assert.Throws(() => - { - m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(serverSideClientId); - }); + string expectedLog = $"[Netcode-Server Sender=0] {serverSideClientId} Only the server can find player objects from other clients."; + LogAssert.Expect(UnityEngine.LogType.Error, expectedLog); + m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(serverSideClientId); } [Test] From 445069f9487411496906b22e2df48a78707f69ac Mon Sep 17 00:00:00 2001 From: Noellie Velez Date: Thu, 9 Apr 2026 11:05:53 +0200 Subject: [PATCH 7/7] Update Changelog --- 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 ed55793aba..573e77b99b 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Changed +- Remove exceptions and replaced Debug usage by NetcodeLog on `NetworkSpawnManager`. (#3933) - Improve performance of `NetworkBehaviour`. (#3915) - Improve performance of `NetworkTransform`. (#3907) - Improve performance of `NetworkRigidbodyBase`. (#3906) @@ -26,6 +27,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Early return when `ChangeOwnership` is called while not spawned. (#3933) - Fixed issue where attempts to use `NetworkLog` when there is no `NetworkManager` instance would result in an exception. (#3917) ### Security