diff --git a/Assets/Mirror/Core/NetworkIdentity.cs b/Assets/Mirror/Core/NetworkIdentity.cs index 8248988cb..7e7082c43 100644 --- a/Assets/Mirror/Core/NetworkIdentity.cs +++ b/Assets/Mirror/Core/NetworkIdentity.cs @@ -264,17 +264,52 @@ internal static void ResetClientStatics() internal static void ResetServerStatics() { + poolNetworkIds = true; + reuseDelay = 1; + + netIdPool.Clear(); nextNetworkId = 1; } /// Gets the NetworkIdentity from the sceneIds dictionary with the corresponding id public static NetworkIdentity GetSceneIdentity(ulong id) => sceneIds[id]; + #region NetworkID Handling + + internal static bool poolNetworkIds = true; + internal static byte reuseDelay = 1; + + internal struct NetworkIdPool + { + public uint poolNetId; + public double timeAvailable; + } + + // pool of NetworkIds that can be reused + internal static readonly Queue netIdPool = new Queue(); static uint nextNetworkId = 1; - internal static uint GetNextNetworkId() => nextNetworkId++; + + internal static uint GetNextNetworkId() + { + if (poolNetworkIds && netIdPool.TryPeek(out NetworkIdPool entry) && entry.timeAvailable < NetworkTime.time) + { + //Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, $"[GetNextNetworkId] Reusing NetworkId {entry.poolNetId}."); + + netIdPool.Dequeue(); + return entry.poolNetId; + } + + return nextNetworkId++; + } /// Resets nextNetworkId = 1 - public static void ResetNextNetworkId() => nextNetworkId = 1; + public static void ResetNextNetworkId() + { + netIdPool.Clear(); + nextNetworkId = 1; + } + + #endregion /// The delegate type for the clientAuthorityCallback. public delegate void ClientAuthorityCallback(NetworkConnectionToClient conn, NetworkIdentity identity, bool authorityState); @@ -628,6 +663,9 @@ void OnDestroy() NetworkServer.Destroy(gameObject); } + if (isServer && poolNetworkIds && netId > 0) + netIdPool.Enqueue(new NetworkIdPool { poolNetId = netId, timeAvailable = NetworkTime.time + reuseDelay }); + if (isLocalPlayer) { // previously there was a bug where isLocalPlayer was @@ -1298,7 +1336,14 @@ internal void Reset() isOwned = false; NotifyAuthority(); - netId = 0; + if (netId > 0) + { + if (poolNetworkIds) + netIdPool.Enqueue(new NetworkIdPool { poolNetId = netId, timeAvailable = NetworkTime.time + reuseDelay }); + + netId = 0; + } + connectionToServer = null; connectionToClient = null; diff --git a/Assets/Mirror/Core/NetworkManager.cs b/Assets/Mirror/Core/NetworkManager.cs index 150daccb4..5648a4d92 100644 --- a/Assets/Mirror/Core/NetworkManager.cs +++ b/Assets/Mirror/Core/NetworkManager.cs @@ -63,6 +63,13 @@ public class NetworkManager : MonoBehaviour // [Tooltip("Client broadcasts 'sendRate' times per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE.")] // public int clientSendRate = 30; // 33 ms + [Header("Network ID Pooling")] + [Tooltip("Reuse network IDs when objects are unspawned or destroyed?")] + public bool poolNetworkIds = true; + [Range(1, 30)] + [Tooltip("Delay in seconds before network IDs are reused")] + public byte reuseDelay = 1; + /// Automatically switch to this scene upon going offline (on start / on disconnect / on shutdown). [Header("Scene Management")] [Scene] @@ -321,6 +328,10 @@ void SetupServer() NetworkServer.disconnectInactiveTimeout = disconnectInactiveTimeout; NetworkServer.exceptionsDisconnect = exceptionsDisconnect; + // Setup reuseable network IDs + NetworkIdentity.poolNetworkIds = poolNetworkIds; + NetworkIdentity.reuseDelay = reuseDelay; + if (runInBackground) Application.runInBackground = true; diff --git a/Assets/Mirror/Editor/NetworkInformationPreview.cs b/Assets/Mirror/Editor/NetworkInformationPreview.cs index ec0a01e5c..b663cf641 100644 --- a/Assets/Mirror/Editor/NetworkInformationPreview.cs +++ b/Assets/Mirror/Editor/NetworkInformationPreview.cs @@ -113,6 +113,8 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) Y = DrawObservers(identity, initialX, Y); + Y = DrawNetworkIDPool(initialX, Y); + _ = DrawOwner(identity, initialX, Y); } @@ -191,6 +193,25 @@ float DrawObservers(NetworkIdentity identity, float initialX, float Y) return Y; } + float DrawNetworkIDPool(float initialX, float Y) + { + if (NetworkIdentity.netIdPool.Count > 0) + { + Rect poolRect = new Rect(initialX, Y + 10, 200, 20); + GUI.Label(poolRect, new GUIContent("Network ID Pool"), styles.componentName); + poolRect.x += 20; + poolRect.y += poolRect.height; + foreach (var entry in NetworkIdentity.netIdPool) + { + GUI.Label(poolRect, $"[{entry.poolNetId}] {entry.timeAvailable:0.000}", styles.labelStyle); + poolRect.y += poolRect.height; + Y = poolRect.y; + } + } + + return Y; + } + float DrawOwner(NetworkIdentity identity, float initialX, float Y) { if (identity.connectionToClient != null)