From b4d1f3240f53adbbd4afde10d7c9e088071d4ed1 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Mon, 26 Oct 2026 15:39:19 -0500 Subject: [PATCH] fix: release unified transport and bootstrap memory between sessions Dispose each connection's send queue when the connection is removed or the transport shuts down, release the safety handle created per received packet, reuse the UnifiedNetcodeTransport component across restarts, and reset the UnifiedBootstrap statics on load so a disabled domain reload does not keep the previous NetworkManager alive. --- .../Components/Helpers/UnifiedBootstrap.cs | 14 +++++++ .../Runtime/Core/NetworkManager.cs | 8 +++- .../Unified/UnifiedNetcodeTransport.cs | 41 ++++++++++++++++--- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs index cce0bdd989..fe193643c2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -31,6 +31,20 @@ internal class UnifiedBootstrap : ClientServerBootstrap // creates its own bootstrap, so the worlds created for other NetworkManagers are registered again. private static readonly List k_CreatedWorlds = new List(); +#if UNITY_EDITOR + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void ResetStaticsOnLoad() + { + Instance = null; + OnInitialized = null; + Port = 7979; + CurrentNetworkManagerForInitialization = null; + LastCreatedWorld = null; + s_WorldCounter = 0; + k_CreatedWorlds.Clear(); + } +#endif + private static void RegisterCreatedWorlds() { for (int i = k_CreatedWorlds.Count - 1; i >= 0; i--) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 4fe5dec349..6f565ef903 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1266,7 +1266,12 @@ internal void Initialize(bool server) NetworkConfig.InitializePrefabs(); if (NetworkConfig.Prefabs.HasGhostPrefabs) { - NetworkConfig.NetworkTransport = gameObject.AddComponent(); + // Reused so that restarting a session does not add another transport. + if (!TryGetComponent(out UnifiedNetcodeTransport unifiedTransport)) + { + unifiedTransport = gameObject.AddComponent(); + } + NetworkConfig.NetworkTransport = unifiedTransport; } #endif #if UNIFIED_NETCODE @@ -1400,6 +1405,7 @@ internal void InitializeNetcodeWorld() TypeManager.Initialize(); UnifiedBootstrap.CurrentNetworkManagerForInitialization = this; new UnifiedBootstrap().Initialize("Default World"); + UnifiedBootstrap.CurrentNetworkManagerForInitialization = null; if (NetcodeWorld != null && NetcodeWorld.IsCreated) { diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index d6efb70e26..860f834571 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -215,6 +215,16 @@ private class ConnectionInfo private Dictionary m_Connections; + private bool RemoveConnection(int connectionId) + { + if (!m_Connections.Remove(connectionId, out ConnectionInfo connectionInfo)) + { + return false; + } + connectionInfo.SendQueue.Dispose(); + return true; + } + internal void DispatchMessage(int connectionId, in FixedBytes1280 buffer) { // The connection is removed on disconnect, which can be ahead of its last messages. @@ -223,7 +233,7 @@ internal void DispatchMessage(int connectionId, in FixedBytes1280 buffer) return; } - using var arr = FixedBytes1280.ToNativeArray(buffer); + var arr = FixedBytes1280.ToNativeArray(buffer); var reader = new DataStreamReader(arr); if (connectionInfo.ReceiveQueue == null) { @@ -233,6 +243,11 @@ internal void DispatchMessage(int connectionId, in FixedBytes1280 buffer) { connectionInfo.ReceiveQueue.PushReader(reader); } +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // Disposing an Allocator.None array does not release its safety handle. The queue has copied the data. + var safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(arr); + CollectionHelper.DisposeSafetyHandle(ref safety); +#endif var message = connectionInfo.ReceiveQueue.PopMessage(); while (message.Count != 0) @@ -278,6 +293,7 @@ public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment