From eab178eb9e1e32ed012065dce56c5753b18eb171 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 24 Mar 2026 12:17:25 -0500 Subject: [PATCH 1/5] fix Fixing exception that could be thrown in NetworkLog if there is no NetworkManager instance. Adding additional check in NetworkObject.OnNetworkBehaviourDestroyed to assure nothing is ever logged about destroying a NetworkBehaviour when shutting down and the NetworkObject is still considered spawned. --- .../Runtime/Core/NetworkObject.cs | 3 ++- .../Runtime/Logging/NetworkLog.cs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index c667af47db..bd518b3048 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -3647,7 +3647,8 @@ internal void OnNetworkBehaviourDestroyed(NetworkBehaviour networkBehaviour) { if (networkBehaviour.IsSpawned && IsSpawned) { - if (NetworkManagerOwner.LogLevel <= LogLevel.Developer) + // Only log this warning if we are not shutting down. + if (!NetworkManagerOwner.ShutdownInProgress && NetworkManagerOwner.LogLevel <= LogLevel.Developer) { NetworkLog.LogWarning($"{nameof(NetworkBehaviour)}-{networkBehaviour.name} is being destroyed while {nameof(NetworkObject)}-{name} is still spawned! (could break state synchronization)"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs index d9319bcfb8..59557cc81a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs +++ b/com.unity.netcode.gameobjects/Runtime/Logging/NetworkLog.cs @@ -113,14 +113,21 @@ private static void LogServer(string message, LogType logType) } } + private const string k_HeaderStart = "Netcode"; private static string Header() { var networkManager = NetworkManagerOverride ??= NetworkManager.Singleton; - if (networkManager.DistributedAuthorityMode) + if (networkManager != null) { - return "Session-Owner"; + if (networkManager.DistributedAuthorityMode) + { + return $"{k_HeaderStart}-Session-Owner"; + } + return $"{k_HeaderStart}-Server"; } - return "Netcode-Server"; + + // If NetworkManager no longer exists, then return the generic header + return k_HeaderStart; } internal static void LogInfoServerLocal(string message, ulong sender) => Debug.Log($"[{Header()} Sender={sender}] {message}"); From 5ac4ef6ce5e0d772175f5363f65d19049989709d Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 24 Mar 2026 12:17:49 -0500 Subject: [PATCH 2/5] test The test that validates the NetworkLog fix. --- .../Runtime/Core/NetworkManager.cs | 8 ++++ .../Tests/Runtime/NetworkLogTests.cs | 47 +++++++++++++++++++ .../Tests/Runtime/NetworkLogTests.cs.meta | 2 + 3 files changed, 57 insertions(+) create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index b5f037c4ac..d8bcfaffff 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1020,6 +1020,14 @@ private void OnTransformParentChanged() NetworkManagerCheckForParent(); } + /// + /// For testing purposes when you need the singleton to be null + /// + internal static void ResetSingleton() + { + Singleton = null; + } + /// /// Set this NetworkManager instance as the static NetworkManager singleton /// diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs new file mode 100644 index 0000000000..dcd43760c9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs @@ -0,0 +1,47 @@ +using System.Collections; +using Unity.Netcode.TestHelpers.Runtime; +using UnityEngine.TestTools; + +namespace Unity.Netcode.RuntimeTests +{ + /// + /// Validates edge cases with + /// + internal class NetworkLogTests : NetcodeIntegrationTest + { + protected override int NumberOfClients => 0; + private bool m_ServerStopped; + + /// + /// Validates that if no exists, + /// you can still use NetworkLog with the caveat when one does + /// not exist it will only log locally. + /// (is topology agnostic) + /// + [UnityTest] + public IEnumerator UseNetworkLogWithNoNetworkManager() + { + m_ServerStopped = false; + var authority = GetAuthorityNetworkManager(); + authority.OnServerStopped += OnServerStopped; + authority.Shutdown(); + yield return WaitForConditionOrTimeOut(()=> m_ServerStopped); + AssertOnTimeout($"Timed out waiting for {nameof(NetworkManager)} to stop!"); + // Assure it is destroyed. + UnityEngine.Object.Destroy(authority); + authority = null; + + // Clear out the singleton to assure NetworkLog has no references to a NetworkManager + NetworkManager.ResetSingleton(); + + // Validate you can use NetworkLog without any NetworkManager instance. + NetworkLog.LogInfoServer($"Test a message to the server with no {nameof(NetworkManager)}."); + // No exceptions thrown is considered passing. + } + + private void OnServerStopped(bool obj) + { + m_ServerStopped = true; + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs.meta new file mode 100644 index 0000000000..fb157e1463 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 984658de4d8c7ef4582baedfa630773f \ No newline at end of file From 668c21241b6e9fba60bc42729edb9e6595c224d8 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 24 Mar 2026 12:23:53 -0500 Subject: [PATCH 3/5] update Adding change log entry for this fix. --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 7f561d4349..26bac20749 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -22,6 +22,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed issue where attempts to use `NetworkLog` when there is no `NetworkManager` instance would result in an exception. (#3917) ### Security From 2dac37c9432b09dd548bebd9752dd554fafee905 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 24 Mar 2026 12:43:26 -0500 Subject: [PATCH 4/5] style adding a single whitespace. --- com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs index dcd43760c9..3307f5ce1e 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkLogTests.cs @@ -25,7 +25,7 @@ public IEnumerator UseNetworkLogWithNoNetworkManager() var authority = GetAuthorityNetworkManager(); authority.OnServerStopped += OnServerStopped; authority.Shutdown(); - yield return WaitForConditionOrTimeOut(()=> m_ServerStopped); + yield return WaitForConditionOrTimeOut(() => m_ServerStopped); AssertOnTimeout($"Timed out waiting for {nameof(NetworkManager)} to stop!"); // Assure it is destroyed. UnityEngine.Object.Destroy(authority); From 3939f319c8873734e098d1a70d01076d917f2dd4 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 24 Mar 2026 14:49:51 -0500 Subject: [PATCH 5/5] fix Adjusting a test for the updated network log header. --- .../Runtime/DistributedAuthority/OwnershipPermissionsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs index 425a9ccc8c..cbd0e67a48 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/OwnershipPermissionsTests.cs @@ -445,7 +445,7 @@ public IEnumerator ChangeOwnershipWithoutObservers() authorityInstance.ChangeOwnership(otherClient.LocalClientId); var senderId = authority.LocalClientId; var receiverId = otherClient.LocalClientId; - LogAssert.Expect(LogType.Warning, $"[Session-Owner Sender={senderId}] [Invalid Owner] Cannot send Ownership change as client-{receiverId} cannot see {authorityInstance.name}! Use NetworkShow first."); + LogAssert.Expect(LogType.Warning, $"[Netcode-Session-Owner Sender={senderId}] [Invalid Owner] Cannot send Ownership change as client-{receiverId} cannot see {authorityInstance.name}! Use NetworkShow first."); Assert.True(authorityInstance.IsOwner, $"[Ownership Check] Client-{senderId} should still own this object!"); // Now re-add the client to the Observers list and try to change ownership