using FishNet.Managing; using FishNet.Managing.Object; using FishNet.Object; using FishNet.Utility.Extension; using GameKit.Dependencies.Utilities; 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 IReadOnlyList>> 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. A new object will be created if no stored objects are available. /// /// PrefabId of the object to return. /// CollectionId of the object to return. /// True if being called on the server side. /// public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, Transform parent = null, Vector3? nullableLocalPosition = null, Quaternion? nullableLocalRotation = null, Vector3? nullableLocalScale = null, bool makeActive = true, bool asServer = true) { if (!_enabled) return GetFromInstantiate(); Stack cache = GetOrCreateCache(collectionId, prefabId); NetworkObject nob = null; //Iterate until nob is populated just in case cache entries have been destroyed. while (nob == null && cache.Count > 0) { nob = cache.Pop(); if (nob != null) { nob.transform.SetParent(parent); nob.transform.SetLocalPositionRotationAndScale(nullableLocalPosition, nullableLocalRotation, nullableLocalScale); if (makeActive) nob.gameObject.SetActive(true); return nob; } } //Fall through, nothing in cache. return GetFromInstantiate(); //Returns a network object via instantation. NetworkObject GetFromInstantiate() { NetworkObject prefab = GetPrefab(prefabId, collectionId, asServer); if (prefab == null) { return null; } else { prefab.transform.OutLocalPropertyValues(nullableLocalPosition, nullableLocalRotation, nullableLocalScale, out Vector3 pos, out Quaternion rot, out Vector3 scale); NetworkObject result = Instantiate(prefab, pos, rot, parent); result.transform.localScale = scale; if (makeActive) result.gameObject.SetActive(true); return result; } } } /// /// Returns a prefab for prefab and collectionId. /// public override NetworkObject GetPrefab(int prefabId, ushort collectionId, bool asServer) { PrefabObjects po = base.NetworkManager.GetPrefabObjects(collectionId, false); return po.GetObject(asServer, prefabId); } /// /// 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.ResetState(asServer); 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 override 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) { NetworkManagerExtensions.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 destroying objects for all collectionIds /// public void ClearPool() { int count = _cache.Count; for (int i = 0; i < count; i++) ClearPool(i); } /// /// Clears a pool destroying objects for collectionId. /// /// CollectionId to clear for. public void ClearPool(int collectionId) { if (collectionId >= _cacheCount) return; Dictionary> dict = _cache[collectionId]; foreach (Stack item in dict.Values) { while (item.Count > 0) { NetworkObject nob = item.Pop(); if (nob != null) Destroy(nob.gameObject); } } dict.Clear(); } /// /// 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; } } }