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;
}
}
}