diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 75687debf2..861e5f5521 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -19,16 +19,16 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Removed - +- Removed un-needed exceptions on `NetworkObject.cs`. (#3867) ### Fixed - +- Fixed `NetworkShow` behavior when it is called twice. (#3867) ### Security ### Obsolete - +- `NotListeningException` is now marked as obsolete as it is not used internally anymore. (#3867) ## [2.10.0] - 2026-03-01 diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index fe8cea525e..f37302c1d2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1458,7 +1458,10 @@ internal NetworkSceneHandle GetSceneOriginHandle() { if (SceneOriginHandle.IsEmpty() && IsSpawned && IsSceneObject != false) { - throw new Exception($"{nameof(GetSceneOriginHandle)} called when {nameof(SceneOriginHandle)} is still zero but the {nameof(NetworkObject)} is already spawned!"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"{nameof(GetSceneOriginHandle)} called when {nameof(SceneOriginHandle)} is still zero but the {nameof(NetworkObject)} is already spawned!"); + } } return !SceneOriginHandle.IsEmpty() ? SceneOriginHandle : gameObject.scene.handle; } @@ -1481,32 +1484,40 @@ public void NetworkShow(ulong clientId) { if (!IsSpawned) { - throw new SpawnStateException("Object is not spawned"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{name}] Attempted NetworkShow while {nameof(NetworkObject)} not spawned."); + } + return; } - if (!HasAuthority) + if (!HasAuthority && !NetworkManagerOwner.DAHost) { if (NetworkManagerOwner.DistributedAuthorityMode) { - throw new NotServerException($"Only the owner-authority can change visibility when distributed authority mode is enabled!"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] Only the owner-authority can change visibility when distributed authority mode is enabled!"); + } + return; } else { - throw new NotServerException("Only the authority can change visibility"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] Only the authority can change visibility!"); + } + return; } } if (Observers.Contains(clientId)) { - if (NetworkManagerOwner.DistributedAuthorityMode) - { - Debug.LogError($"The object {name} is already visible to Client-{clientId}!"); - return; - } - else + if (NetworkManagerOwner.LogLevel <= LogLevel.Developer) { - throw new NotServerException("Only server can change visibility"); + NetworkLog.LogWarning($"[{name}] {nameof(NetworkObject)} is already visible to Client-{clientId}! (ignoring)"); } + return; } if (CheckObjectVisibility != null && !CheckObjectVisibility(clientId)) @@ -1556,8 +1567,8 @@ public static void NetworkShow(List networkObjects, ulong clientI /// /// Usage: Use to stop sending updates to the targeted client, "netcode invisible", for a currently visible .
///
- /// Dynamically Spawned: s will be despawned and destroyed on the targeted client's side.
- /// In-Scene Placed: s will only be despawned on the targeted client's side.
+ /// Dynamically Spawned: s will be de-spawned and destroyed on the targeted client's side.
+ /// In-Scene Placed: s will only be de-spawned on the targeted client's side.
///
/// See Also:
///
@@ -1568,18 +1579,30 @@ public void NetworkHide(ulong clientId) { if (!IsSpawned) { - throw new SpawnStateException("Object is not spawned"); + if (NetworkManager.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{name}] Attempted NetworkHide while {nameof(NetworkObject)} is not spawned."); + } + return; } if (!HasAuthority && !NetworkManagerOwner.DAHost) { if (NetworkManagerOwner.DistributedAuthorityMode) { - throw new NotServerException($"Only the owner-authority can change visibility when distributed authority mode is enabled!"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] Only the owner-authority can change visibility when distributed authority mode is enabled!"); + } + return; } else { - throw new NotServerException("Only the authority can change visibility"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] Only the authority can change visibility!"); + } + return; } } @@ -1589,9 +1612,9 @@ public void NetworkHide(ulong clientId) { if (NetworkManagerOwner.LogLevel <= LogLevel.Developer) { - Debug.LogWarning($"{name} is already hidden from Client-{clientId}! (ignoring)"); - return; + NetworkLog.LogWarning($"[{name}] {nameof(NetworkObject)} already hidden from Client-{clientId}! (ignoring)"); } + return; } Observers.Remove(clientId); @@ -1724,18 +1747,30 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla if (!NetworkManagerOwner.IsListening) { - throw new NotListeningException($"{nameof(NetworkManagerOwner)} is not listening, start a server or host before spawning objects"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] {nameof(NetworkManagerOwner)} is not listening, start a server or host before spawning objects."); + } + return; } if ((!NetworkManagerOwner.IsServer && !NetworkManagerOwner.DistributedAuthorityMode) || (NetworkManagerOwner.DistributedAuthorityMode && !NetworkManagerOwner.LocalClient.IsSessionOwner && NetworkManagerOwner.LocalClientId != ownerClientId)) { if (NetworkManagerOwner.DistributedAuthorityMode) { - throw new NotServerException($"When distributed authority mode is enabled, you can only spawn NetworkObjects that belong to the local instance! Local instance id {NetworkManagerOwner.LocalClientId} is not the same as the assigned owner id: {ownerClientId}!"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] When distributed authority mode is enabled, you can only spawn NetworkObjects that belong to the local instance! Local instance id {NetworkManagerOwner.LocalClientId} is not the same as the assigned owner id: {ownerClientId}!"); + } + return; } else { - throw new NotServerException($"Only server can spawn {nameof(NetworkObject)}s"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] Only server can spawn {nameof(NetworkObject)}s."); + } + return; } } @@ -2256,7 +2291,10 @@ private void OnTransformParentChanged() return; } transform.parent = m_CachedParent; - Debug.LogException(new NotListeningException($"[{name}] {nameof(networkManager)} is not listening, start a server or host before re-parenting")); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"[{name}] {nameof(networkManager)} is not listening, start a server or host before re-parenting."); + } return; } @@ -2273,7 +2311,10 @@ private void OnTransformParentChanged() else { transform.parent = m_CachedParent; - Debug.LogException(new SpawnStateException($"[{name}] {nameof(NetworkObject)} can only be re-parented after being spawned")); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{name}] {nameof(NetworkObject)} can only be re-parented after being spawned!"); + } } return; } @@ -2293,7 +2334,7 @@ private void OnTransformParentChanged() } else { - Debug.LogException(new NotServerException($"[{name}] Only the server can re-parent {nameof(NetworkObject)}s")); + NetworkLog.LogError($"[{name}] Only the server can re-parent {nameof(NetworkObject)}s."); } } return; @@ -2307,14 +2348,20 @@ private void OnTransformParentChanged() { transform.parent = m_CachedParent; AuthorityAppliedParenting = false; - Debug.LogException(new InvalidParentException($"[{name}] Invalid parenting, {nameof(NetworkObject)} moved under a non-{nameof(NetworkObject)} parent")); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{name}] Invalid parenting, {nameof(NetworkObject)} moved under a non-{nameof(NetworkObject)} parent"); + } return; } else if (!parentObject.IsSpawned) { transform.parent = m_CachedParent; AuthorityAppliedParenting = false; - Debug.LogException(new SpawnStateException($"[{name}] {nameof(NetworkObject)} can only be re-parented under another spawned {nameof(NetworkObject)}")); + if (NetworkManagerOwner.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{name}] {nameof(NetworkObject)} can only be re-parented under another spawned {nameof(NetworkObject)}."); + } return; } @@ -2536,7 +2583,10 @@ internal void InvokeBehaviourNetworkSpawn() { if (!childBehaviour.gameObject.activeInHierarchy) { - Debug.LogWarning($"{GenerateDisabledNetworkBehaviourWarning(childBehaviour)}"); + if (NetworkManagerOwner.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogWarning($"{GenerateDisabledNetworkBehaviourWarning(childBehaviour)}"); + } continue; } childBehaviour.InternalOnNetworkSpawn(); @@ -3327,7 +3377,11 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject, // Spawn the NetworkObject if (networkObject.IsSpawned) { - throw new SpawnStateException($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!"); + if (NetworkManager.Singleton.LogLevel <= LogLevel.Error) + { + NetworkLog.LogErrorServer($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!"); + } + return null; } // Invoke the non-authority local spawn method diff --git a/com.unity.netcode.gameobjects/Runtime/Exceptions/NotListeningException.cs b/com.unity.netcode.gameobjects/Runtime/Exceptions/NotListeningException.cs index f345432cc0..9c2c52e520 100644 --- a/com.unity.netcode.gameobjects/Runtime/Exceptions/NotListeningException.cs +++ b/com.unity.netcode.gameobjects/Runtime/Exceptions/NotListeningException.cs @@ -5,6 +5,7 @@ namespace Unity.Netcode /// /// Exception thrown when the operation require NetworkManager to be listening. /// + [Obsolete("Not used anymore.")] public class NotListeningException : Exception { /// diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs index 6dab1b7ba0..6c09212a18 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkShowHideTests.cs @@ -892,5 +892,32 @@ public IEnumerator NetworkShowAndChangeOwnership() AssertOnTimeout($"Timed out waiting for clients-{m_NewOwner.LocalClientId} to gain ownership of object {m_OwnershipNetworkObject.NetworkObjectId}!"); VerboseDebug($"Client {m_NewOwner.LocalClientId} now owns object {m_OwnershipNetworkObject.NetworkObjectId}!"); } + + [UnityTest] + public IEnumerator DuplicateHideShowTest() + { + var authority = GetAuthorityNetworkManager(); + var nonAuthority = GetNonAuthorityNetworkManager(); + m_ClientId0 = nonAuthority.LocalClientId; + ShowHideObject.ClientTargetedNetworkObjects.Clear(); + ShowHideObject.ClientIdToTarget = m_ClientId0; + ShowHideObject.Silent = true; + + var spawnedObject1 = SpawnObject(m_PrefabToSpawn, authority); + m_NetSpawnedObject1 = spawnedObject1.GetComponent(); + + m_NetSpawnedObject1.GetComponent().MyNetworkVariable.Value++; + m_NetSpawnedObject1.NetworkHide(m_ClientId0); + m_NetSpawnedObject1.NetworkHide(m_ClientId0); + + yield return WaitForConditionOrTimeOut(() => !nonAuthority.SpawnManager.SpawnedObjects.ContainsKey(m_NetSpawnedObject1.NetworkObjectId)); + AssertOnTimeout($"NetworkObject {m_NetSpawnedObject1.name} is still spawned on client-{nonAuthority.LocalClientId} after timeout!"); + + m_NetSpawnedObject1.NetworkShow(m_ClientId0); + m_NetSpawnedObject1.NetworkShow(m_ClientId0); + + yield return WaitForConditionOrTimeOut(() => nonAuthority.SpawnManager.SpawnedObjects.ContainsKey(m_NetSpawnedObject1.NetworkObjectId)); + AssertOnTimeout($"NetworkObject {m_NetSpawnedObject1.name} is not yet spawned on client-{nonAuthority.LocalClientId} after timeout!"); + } } } diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs index a8eda1a829..c080cac7c5 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs @@ -207,7 +207,7 @@ public void SetupSet(Transform rootTransform, int setIndex, NetworkManager netwo Assert.That(m_Pickup_NetObjs[setIndex], Is.Not.Null); Assert.That(m_Pickup_Back_NetObjs[setIndex], Is.Not.Null); - LogAssert.Expect(LogType.Exception, new Regex("start a server or host", RegexOptions.IgnoreCase)); + LogAssert.Expect(LogType.Error, new Regex("start a server or host", RegexOptions.IgnoreCase)); var cachedParent = m_Cube_NetObjs[setIndex].parent; m_Cube_NetObjs[setIndex].parent = m_Pickup_NetObjs[setIndex]; Assert.That(m_Cube_NetObjs[setIndex].parent, Is.EqualTo(cachedParent), $"Transform {m_Cube_NetObjs[setIndex].parent.name} is not equal to transform {cachedParent.name}");