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)