using FishNet.Managing.Object; using FishNet.Object; using FishNet.Utility.Extension; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; namespace FishNet.Utility.Performance { public class DefaultObjectPool : ObjectPool { #region Public. /// /// Cache for pooled NetworkObjects. /// public IReadOnlyCollection>> Cache => _cache; private List>> _cache = new List>>(); #endregion #region Serialized. /// /// True if to use object pooling. /// [Tooltip("True if to use object pooling.")] [SerializeField] private bool _enabled = true; #endregion #region Private. /// /// Current count of the cache collection. /// private int _cacheCount = 0; #endregion /// /// Returns an object that has been stored with a collectionId of 0. A new object will be created if no stored objects are available. /// /// PrefabId of the object to return. /// True if being called on the server side. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override NetworkObject RetrieveObject(int prefabId, bool asServer) { return RetrieveObject(prefabId, 0, asServer); } /// /// Returns an object that has been stored. A new object will be created if no stored objects are available. /// /// PrefabId of the object to return. /// CollectionId of the prefab. /// True if being called on the server side. /// public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, bool asServer) { PrefabObjects po = base.NetworkManager.GetPrefabObjects(collectionId, false); //Quick exit/normal retrieval when not using pooling. if (!_enabled) { NetworkObject prefab = po.GetObject(asServer, prefabId); return Instantiate(prefab); } Stack cache = GetOrCreateCache(collectionId, prefabId); NetworkObject nob; //Iterate until nob is populated just in case cache entries have been destroyed. do { if (cache.Count == 0) { NetworkObject prefab = po.GetObject(asServer, prefabId); /* A null nob should never be returned from spawnables. This means something * else broke, likely unrelated to the object pool. */ nob = Instantiate(prefab); //Can break instantly since we know nob is not null. break; } else { nob = cache.Pop(); } } while (nob == null); nob.gameObject.SetActive(true); return nob; } /// /// Stores an object into the pool. /// /// Object to store. /// True if being called on the server side. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void StoreObject(NetworkObject instantiated, bool asServer) { //Pooling is not enabled. if (!_enabled) { Destroy(instantiated.gameObject); return; } instantiated.gameObject.SetActive(false); instantiated.ResetForObjectPool(); Stack cache = GetOrCreateCache(instantiated.SpawnableCollectionId, instantiated.PrefabId); cache.Push(instantiated); } /// /// Instantiates a number of objects and adds them to the pool. /// /// Prefab to cache. /// Quantity to spawn. /// True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects. public void CacheObjects(NetworkObject prefab, int count, bool asServer) { if (!_enabled) return; if (count <= 0) return; if (prefab == null) return; if (prefab.PrefabId == NetworkObject.UNSET_PREFABID_VALUE) { InstanceFinder.NetworkManager.LogError($"Pefab {prefab.name} has an invalid prefabId and cannot be cached."); return; } Stack cache = GetOrCreateCache(prefab.SpawnableCollectionId, prefab.PrefabId); for (int i = 0; i < count; i++) { NetworkObject nob = Instantiate(prefab); nob.gameObject.SetActive(false); cache.Push(nob); } } /// /// Clears pools for all collectionIds /// public void ClearPool() { int count = _cache.Count; for (int i = 0; i < count; i++) ClearPool(i); } /// /// Clears a pool for collectionId. /// /// CollectionId to clear for. public void ClearPool(int collectionId) { if (collectionId >= _cacheCount) return; Dictionary> dict = _cache[collectionId]; //Convert to a list from the stack so we do not modify the stack directly. ListCache nobCache = ListCaches.GetNetworkObjectCache(); foreach (Stack item in dict.Values) { while (item.Count > 0) nobCache.AddValue(item.Pop()); } } /// /// Gets a cache for an id or creates one if does not exist. /// /// /// private Stack GetOrCreateCache(int collectionId, int prefabId) { if (collectionId >= _cacheCount) { //Add more to the cache. while (_cache.Count <= collectionId) { Dictionary> dict = new Dictionary>(); _cache.Add(dict); } _cacheCount = collectionId; } Dictionary> dictionary = _cache[collectionId]; Stack cache; //No cache for prefabId yet, make one. if (!dictionary.TryGetValueIL2CPP(prefabId, out cache)) { cache = new Stack(); dictionary[prefabId] = cache; } return cache; } } }