Add StickGame Assets

This commit is contained in:
Dzejkobik007
2024-03-24 22:21:16 +01:00
parent 5a5812c0c7
commit 6c8b523d1f
6643 changed files with 596260 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
namespace FishNet.Utility.Constant
{
internal static class UtilityConstants
{
public const string CODEGEN_ASSEMBLY_NAME = "Unity.FishNet.CodeGen";
public const string GENERATED_ASSEMBLY_NAME = "FishNet.Generated";
public const string DEMOS_ASSEMBLY_NAME = "FishNet.Demos";
public const string TEST_ASSEMBLY_NAME = "FishNet.Test";
public const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f2a3c23b44e4ef4e9783ef53ec0d5da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0d008bcb408806c45980331a665c65a7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using FishNet.Documenting;
using FishNet.Managing;
using FishNet.Object;
namespace FishNet.Utility.Extension
{
[APIExclude]
public static class NetworksFN
{
/// <summary>
/// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side.
/// </summary>
public static bool DoubleLogic(this NetworkObject nob, bool asServer) => (!asServer && nob.NetworkManager.IsServer);
/// <summary>
/// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side.
/// </summary>
public static bool DoubleLogic(this NetworkManager manager, bool asServer) => (!asServer && manager.IsServer);
/// <summary>
/// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side.
/// </summary>
public static bool DoubleLogic(this NetworkBehaviour nb, bool asServer) => (!asServer && nb.NetworkManager.IsServer);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbd3adaae5d34a14ab9cf68726b3bd3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using FishNet.Object;
using GameKit.Utilities;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace FishNet.Utility.Extension
{
public static class Scenes
{
/// <summary>
/// Gets all NetworkObjects in a scene.
/// </summary>
/// <param name="s">Scene to get objects in.</param>
/// <param name="firstOnly">True to only return the first NetworkObject within an object chain. False will return nested NetworkObjects.</param>
/// <param name="cache">ListCache of found NetworkObjects.</param>
/// <returns></returns>
public static void GetSceneNetworkObjects(Scene s, bool firstOnly, ref List<NetworkObject> result)
{
List<NetworkObject> nobCacheA = CollectionCaches<NetworkObject>.RetrieveList();
List<NetworkObject> nobCacheB = CollectionCaches<NetworkObject>.RetrieveList();
List<GameObject> gameObjectCache = CollectionCaches<GameObject>.RetrieveList();
//Iterate all root objects for the scene.
s.GetRootGameObjects(gameObjectCache);
foreach (GameObject go in gameObjectCache)
{
//Get NetworkObjects within children of each root.
go.GetComponentsInChildren<NetworkObject>(true, nobCacheA);
//If network objects are found.
if (nobCacheA.Count > 0)
{
//Add only the first networkobject
if (firstOnly)
{
/* The easiest way to see if a nob is nested is to
* get nobs in parent and if the count is greater than 1, then
* it is nested. The technique used here isn't exactly fast but
* it will only occur during scene loads, so I'm trading off speed
* for effort and readability. */
foreach (NetworkObject nob in nobCacheA)
{
nob.GetComponentsInParent<NetworkObject>(true, nobCacheB);
//No extra nobs, only this one.
if (nobCacheB.Count == 1)
result.Add(nob);
}
}
//Not first only, add them all.
else
{
result.AddRange(nobCacheA);
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a02f3d03f737e304e9854278f4e9211d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
using FishNet.Documenting;
using FishNet.Object;
using UnityEngine;
namespace FishNet.Utility.Extension
{
[APIExclude]
public static class TransformFN
{
/// <summary>
/// Sets values of TransformProperties to a transforms world properties.
/// </summary>
public static TransformProperties GetWorldProperties(this Transform t)
{
TransformProperties tp = new TransformProperties(t.position, t.rotation, t.localScale);
return tp;
}
/// <summary>
/// Sets values of TransformPropertiesCls to a transforms world properties.
/// </summary>
public static void SetWorldProperties(this TransformPropertiesCls tp, Transform t)
{
tp.Position = t.position;
tp.Rotation = t.rotation;
tp.LocalScale = t.localScale;
}
/// <summary>
/// Sets the offset values of target from a transform.
/// </summary>
/// <param name="pos">Position offset result.</param>
/// <param name="rot">Rotation offset result.</param>
public static void SetTransformOffsets(this Transform t, Transform target, ref Vector3 pos, ref Quaternion rot)
{
if (target == null)
return;
pos = (target.position - t.position);
rot = (target.rotation * Quaternion.Inverse(t.rotation));
}
/// <summary>
/// Sets the offset values of target from a transform.
/// </summary>
/// <param name="pos">Position offset result.</param>
/// <param name="rot">Rotation offset result.</param>
public static TransformProperties GetTransformOffsets(this Transform t, Transform target)
{
if (target == null)
return default;
return new TransformProperties(
(target.position - t.position),
(target.rotation * Quaternion.Inverse(t.rotation)),
(target.localScale - t.localScale)
);
}
/// <summary>
/// Sets local position and rotation for a transform.
/// </summary>
public static void SetLocalPositionAndRotation(this Transform t, Vector3 pos, Quaternion rot)
{
t.localPosition = pos;
t.localRotation = rot;
}
/// <summary>
/// Sets local position, rotation, and scale for a transform.
/// </summary>
public static void SetLocalPositionRotationAndScale(this Transform t, Vector3 pos, Quaternion rot, Vector3 scale)
{
t.localPosition = pos;
t.localRotation = rot;
t.localScale = scale;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d311fc1bf09b9e4fbc5a17a9c50ab0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b315d9102b18b541b0b2346c9ad63ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,202 @@
using System;
namespace FishNet.Utility.Performance
{
/// <summary>
/// Unity 2022 has a bug where codegen will not compile when referencing a Queue type,
/// while also targeting .Net as the framework API.
/// As a work around this class is used for queues instead.
/// </summary>
public class BasicQueue<T>
{
/// <summary>
/// Maximum size of the collection.
/// </summary>
public int Capacity => Collection.Length;
/// <summary>
/// Number of elements in the queue.
/// </summary>
public int Count => _written;
/// <summary>
/// Collection containing data.
/// </summary>
private T[] Collection = new T[4];
/// <summary>
/// Current write index of the collection.
/// </summary>
public int WriteIndex { get; private set; }
/// <summary>
/// Buffer for resizing.
/// </summary>
private T[] _resizeBuffer = new T[0];
/// <summary>
/// Read position of the next Dequeue.
/// </summary>
private int _read;
/// <summary>
/// Length of the queue.
/// </summary>
private int _written;
/// <summary>
/// Enqueues an entry.
/// </summary>
/// <param name="data"></param>
public void Enqueue(T data)
{
if (_written == Collection.Length)
Resize();
if (WriteIndex >= Collection.Length)
WriteIndex = 0;
Collection[WriteIndex] = data;
WriteIndex++;
_written++;
}
/// <summary>
/// Tries to dequeue the next entry.
/// </summary>
/// <param name="result">Dequeued entry.</param>
/// <returns>True if an entry existed to dequeue.</returns>
public bool TryDequeue(out T result)
{
if (_written == 0)
{
result = default;
return false;
}
result = Dequeue();
return true;
}
/// <summary>
/// Dequeues the next entry.
/// </summary>
/// <returns></returns>
public T Dequeue()
{
if (_written == 0)
throw new Exception($"Queue of type {typeof(T).Name} is empty.");
T result = Collection[_read];
_written--;
_read++;
if (_read >= Collection.Length)
_read = 0;
return result;
}
/// <summary>
/// Tries to peek the next entry.
/// </summary>
/// <param name="result">Peeked entry.</param>
/// <returns>True if an entry existed to peek.</returns>
public bool TryPeek(out T result)
{
if (_written == 0)
{
result = default;
return false;
}
result = Peek();
return true;
}
/// <summary>
/// Peeks the next queue entry.
/// </summary>
/// <returns></returns>
public T Peek()
{
if (_written == 0)
throw new Exception($"Queue of type {typeof(T).Name} is empty.");
return Collection[_read];
}
/// <summary>
/// Clears the queue.
/// </summary>
/// <param name="makeDefault">True to make buffer entries default.</param>
public void Clear()
{
_read = 0;
WriteIndex = 0;
_written = 0;
DefaultCollection(Collection);
DefaultCollection(_resizeBuffer);
void DefaultCollection(T[] array)
{
int count = array.Length;
for (int i = 0; i < count; i++)
array[i] = default;
}
}
/// <summary>
/// Doubles the queue size.
/// </summary>
private void Resize()
{
int length = _written;
int doubleLength = (length * 2);
int read = _read;
/* Make sure copy array is the same size as current
* and copy contents into it. */
//Ensure large enough to fit contents.
T[] resizeBuffer = _resizeBuffer;
if (resizeBuffer.Length < doubleLength)
Array.Resize(ref resizeBuffer, doubleLength);
//Copy from the read of queue first.
int copyLength = (length - read);
Array.Copy(Collection, read, resizeBuffer, 0, copyLength);
/* If read index was higher than 0
* then copy remaining data as well from 0. */
if (read > 0)
Array.Copy(Collection, 0, resizeBuffer, copyLength, read);
//Set _array to resize.
Collection = resizeBuffer;
//Reset positions.
_read = 0;
WriteIndex = length;
}
/// <summary>
/// Returns value in actual index as it relates to simulated index.
/// </summary>
/// <param name="simulatedIndex">Simulated index to return. A value of 0 would return the first simulated index in the collection.</param>
/// <returns></returns>
public T this[int simulatedIndex]
{
get
{
int offset = (Capacity - _written) + simulatedIndex + WriteIndex;
if (offset >= Capacity)
offset -= Capacity;
return Collection[offset];
}
set
{
int offset = (Capacity - _written) + simulatedIndex + WriteIndex;
if (offset >= Capacity)
offset -= Capacity;
Collection[offset] = value;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c0a46ad871780b45bfe317c8f904322
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
namespace FishNet.Utility.Performance
{
/// <summary>
/// Retrieves and stores byte arrays using a pooling system.
/// </summary>
public static class ByteArrayPool
{
/// <summary>
/// Stored byte arrays.
/// </summary>
private static Queue<byte[]> _byteArrays = new Queue<byte[]>();
/// <summary>
/// Returns a byte array which will be of at lesat minimum length. The returned array must manually be stored.
/// </summary>
public static byte[] Retrieve(int minimumLength)
{
byte[] result = null;
if (_byteArrays.Count > 0)
result = _byteArrays.Dequeue();
int doubleMinimumLength = (minimumLength * 2);
if (result == null)
result = new byte[doubleMinimumLength];
else if (result.Length < minimumLength)
Array.Resize(ref result, doubleMinimumLength);
return result;
}
/// <summary>
/// Stores a byte array for re-use.
/// </summary>
public static void Store(byte[] buffer)
{
/* Holy cow that's a lot of buffered
* buffers. This wouldn't happen under normal
* circumstances but if the user is stress
* testing connections in one executable perhaps. */
if (_byteArrays.Count > 300)
return;
_byteArrays.Enqueue(buffer);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7620a5e6fedc18408f8f04821b35bbd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,255 @@
using FishNet.Managing.Object;
using FishNet.Object;
using GameKit.Utilities;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Utility.Performance
{
public class DefaultObjectPool : ObjectPool
{
#region Public.
/// <summary>
/// Cache for pooled NetworkObjects.
/// </summary> //Remove on 2024/01/01 Convert to IReadOnlyList.
public IReadOnlyCollection<Dictionary<int, Stack<NetworkObject>>> Cache => _cache;
private List<Dictionary<int, Stack<NetworkObject>>> _cache = new List<Dictionary<int, Stack<NetworkObject>>>();
#endregion
#region Serialized.
/// <summary>
/// True if to use object pooling.
/// </summary>
[Tooltip("True if to use object pooling.")]
[SerializeField]
private bool _enabled = true;
#endregion
#region Private.
/// <summary>
/// Current count of the cache collection.
/// </summary>
private int _cacheCount = 0;
#endregion
/// <summary>
/// Returns an object that has been stored with a collectionId of 0. A new object will be created if no stored objects are available.
/// </summary>
/// <param name="prefabId">PrefabId of the object to return.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] //Remove on 2024/01/01.
#pragma warning disable CS0672 // Member overrides obsolete member
public override NetworkObject RetrieveObject(int prefabId, bool asServer)
#pragma warning restore CS0672 // Member overrides obsolete member
{
return RetrieveObject(prefabId, 0, asServer);
}
/// <summary>
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
/// </summary>
/// <param name="prefabId">PrefabId of the object to return.</param>
/// <param name="collectionId">CollectionId of the prefab.</param>
/// <param name="position">Position for object before enabling it.</param>
/// <param name="rotation">Rotation for object before enabling it.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, Vector3 position, Quaternion rotation, bool asServer)
{
PrefabObjects po = base.NetworkManager.GetPrefabObjects<PrefabObjects>(collectionId, false);
//Quick exit/normal retrieval when not using pooling.
if (!_enabled)
{
NetworkObject prefab = po.GetObject(asServer, prefabId);
return Instantiate(prefab, position, rotation);
}
Stack<NetworkObject> 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, position, rotation);
//Can break instantly since we know nob is not null.
break;
}
else
{
nob = cache.Pop();
if (nob != null)
nob.transform.SetPositionAndRotation(position, rotation);
}
} while (nob == null);
nob.gameObject.SetActive(true);
return nob;
}
/// <summary>
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
/// </summary>
/// <param name="prefabId">PrefabId of the object to return.</param>
/// <param name="collectionId">CollectionId of the prefab.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, bool asServer)
{
PrefabObjects po = base.NetworkManager.GetPrefabObjects<PrefabObjects>(collectionId, false);
//Quick exit/normal retrieval when not using pooling.
if (!_enabled)
{
NetworkObject prefab = po.GetObject(asServer, prefabId);
return Instantiate(prefab);
}
Stack<NetworkObject> 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;
}
/// <summary>
/// Stores an object into the pool.
/// </summary>
/// <param name="instantiated">Object to store.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
[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();
Stack<NetworkObject> cache = GetOrCreateCache(instantiated.SpawnableCollectionId, instantiated.PrefabId);
cache.Push(instantiated);
}
/// <summary>
/// Instantiates a number of objects and adds them to the pool.
/// </summary>
/// <param name="prefab">Prefab to cache.</param>
/// <param name="count">Quantity to spawn.</param>
/// <param name="asServer">True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects.</param>
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)
{
InstanceFinder.NetworkManager.LogError($"Pefab {prefab.name} has an invalid prefabId and cannot be cached.");
return;
}
Stack<NetworkObject> cache = GetOrCreateCache(prefab.SpawnableCollectionId, prefab.PrefabId);
for (int i = 0; i < count; i++)
{
NetworkObject nob = Instantiate(prefab);
nob.gameObject.SetActive(false);
cache.Push(nob);
}
}
/// <summary>
/// Clears pools destroying objects for all collectionIds
/// </summary>
public void ClearPool()
{
int count = _cache.Count;
for (int i = 0; i < count; i++)
ClearPool(i);
}
/// <summary>
/// Clears a pool destroying objects for collectionId.
/// </summary>
/// <param name="collectionId">CollectionId to clear for.</param>
public void ClearPool(int collectionId)
{
if (collectionId >= _cacheCount)
return;
Dictionary<int, Stack<NetworkObject>> dict = _cache[collectionId];
foreach (Stack<NetworkObject> item in dict.Values)
{
while (item.Count > 0)
{
NetworkObject nob = item.Pop();
if (nob != null)
Destroy(nob.gameObject);
}
}
dict.Clear();
}
/// <summary>
/// Gets a cache for an id or creates one if does not exist.
/// </summary>
/// <param name="prefabId"></param>
/// <returns></returns>
private Stack<NetworkObject> GetOrCreateCache(int collectionId, int prefabId)
{
if (collectionId >= _cacheCount)
{
//Add more to the cache.
while (_cache.Count <= collectionId)
{
Dictionary<int, Stack<NetworkObject>> dict = new Dictionary<int, Stack<NetworkObject>>();
_cache.Add(dict);
}
_cacheCount = collectionId;
}
Dictionary<int, Stack<NetworkObject>> dictionary = _cache[collectionId];
Stack<NetworkObject> cache;
//No cache for prefabId yet, make one.
if (!dictionary.TryGetValueIL2CPP(prefabId, out cache))
{
cache = new Stack<NetworkObject>();
dictionary[prefabId] = cache;
}
return cache;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cb13d174096685549b1d6a94d726ff7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,351 @@
using FishNet.Connection;
using FishNet.Object;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Utility.Performance
{
/// <summary>
/// Various ListCache instances that may be used on the Unity thread.
/// </summary>
[Obsolete("ListCache has been discovered potentially contain a small memory leak depending on the type being cached. Use ObjectCaches, ResettableObjectCaches, CollectionCaches, ResettableCollectionCaches instead.")] //remove on 2023/01/01
public static class ListCaches
{
/// <summary>
/// Cache collection for NetworkObjects.
/// </summary>
private static Stack<ListCache<NetworkObject>> _networkObjectCaches = new Stack<ListCache<NetworkObject>>();
/// <summary>
/// Cache collection for NetworkBehaviours.
/// </summary>
private static Stack<ListCache<NetworkBehaviour>> _networkBehaviourCaches = new Stack<ListCache<NetworkBehaviour>>();
/// <summary>
/// Cache collection for NetworkObjects.
/// </summary>
private static Stack<ListCache<Transform>> _transformCaches = new Stack<ListCache<Transform>>();
/// <summary>
/// Cache collection for NetworkConnections.
/// </summary>
private static Stack<ListCache<NetworkConnection>> _networkConnectionCaches = new Stack<ListCache<NetworkConnection>>();
/// <summary>
/// Cache collection for ints.
/// </summary>
private static Stack<ListCache<int>> _intCaches = new Stack<ListCache<int>>();
#region GetCache.
[Obsolete("Use RetrieveNetworkObjectCache().")] //Remove on 2024/01/01
public static ListCache<NetworkObject> GetNetworkObjectCache() => RetrieveNetworkObjectCache();
/// <summary>
/// Returns a NetworkObject cache. Use StoreCache to return the cache.
/// </summary>
/// <returns></returns>
public static ListCache<NetworkObject> RetrieveNetworkObjectCache()
{
ListCache<NetworkObject> result;
if (_networkObjectCaches.Count == 0)
result = new ListCache<NetworkObject>();
else
result = _networkObjectCaches.Pop();
return result;
}
[Obsolete("Use RetrieveNetworkConnectionCache().")] //Remove on 2024/01/01
public static ListCache<NetworkConnection> GetNetworkConnectionCache() => RetrieveNetworkConnectionCache();
/// <summary>
/// Returns a NetworkConnection cache. Use StoreCache to return the cache.
/// </summary>
/// <returns></returns>
public static ListCache<NetworkConnection> RetrieveNetworkConnectionCache()
{
ListCache<NetworkConnection> result;
if (_networkConnectionCaches.Count == 0)
result = new ListCache<NetworkConnection>();
else
result = _networkConnectionCaches.Pop();
return result;
}
[Obsolete("Use RetrieveTransformCache().")] //Remove on 2024/01/01
public static ListCache<Transform> GetTransformCache() => RetrieveTransformCache();
/// <summary>
/// Returns a Transform cache. Use StoreCache to return the cache.
/// </summary>
/// <returns></returns>
public static ListCache<Transform> RetrieveTransformCache()
{
ListCache<Transform> result;
if (_transformCaches.Count == 0)
result = new ListCache<Transform>();
else
result = _transformCaches.Pop();
return result;
}
[Obsolete("Use RetrieveNetworkBehaviourCache().")] //Remove on 2024/01/01
public static ListCache<NetworkBehaviour> GetNetworkBehaviourCache() => RetrieveNetworkBehaviourCache();
/// <summary>
/// Returns a NetworkBehaviour cache. Use StoreCache to return the cache.
/// </summary>
/// <returns></returns>
public static ListCache<NetworkBehaviour> RetrieveNetworkBehaviourCache()
{
ListCache<NetworkBehaviour> result;
if (_networkBehaviourCaches.Count == 0)
result = new ListCache<NetworkBehaviour>();
else
result = _networkBehaviourCaches.Pop();
return result;
}
[Obsolete("Use RetrieveGetIntCache().")] //Remove on 2024/01/01
public static ListCache<int> GetIntCache() => RetrieveIntCache();
/// <summary>
/// Returns an int cache. Use StoreCache to return the cache.
/// </summary>
/// <returns></returns>
public static ListCache<int> RetrieveIntCache()
{
ListCache<int> result;
if (_intCaches.Count == 0)
result = new ListCache<int>();
else
result = _intCaches.Pop();
return result;
}
#endregion
#region StoreCache.
/// <summary>
/// Stores a NetworkObject cache.
/// </summary>
/// <param name="cache"></param>
public static void StoreCache(ListCache<NetworkObject> cache)
{
cache.Reset();
_networkObjectCaches.Push(cache);
}
/// <summary>
/// Stores a NetworkConnection cache.
/// </summary>
/// <param name="cache"></param>
public static void StoreCache(ListCache<NetworkConnection> cache)
{
cache.Reset();
_networkConnectionCaches.Push(cache);
}
/// <summary>
/// Stores a Transform cache.
/// </summary>
/// <param name="cache"></param>
public static void StoreCache(ListCache<Transform> cache)
{
cache.Reset();
_transformCaches.Push(cache);
}
/// <summary>
/// Stores a NetworkBehaviour cache.
/// </summary>
/// <param name="cache"></param>
public static void StoreCache(ListCache<NetworkBehaviour> cache)
{
cache.Reset();
_networkBehaviourCaches.Push(cache);
}
/// <summary>
/// Stores an int cache.
/// </summary>
/// <param name="cache"></param>
public static void StoreCache(ListCache<int> cache)
{
cache.Reset();
_intCaches.Push(cache);
}
#endregion
}
/// <summary>
/// Creates a reusable cache of T which auto expands.
/// </summary>
[Obsolete("Use CollectionCache<T> instead.")] //Remove on 2024/01/01.
public class ListCache<T>
{
#region Public.
/// <summary>
/// Collection cache is for.
/// </summary>
public List<T> Collection = new List<T>();
/// <summary>
/// Entries currently written.
/// </summary>
public int Written => Collection.Count;
#endregion
#region Private.
/// <summary>
/// Cache for type.
/// </summary>
private Stack<T> _cache = new Stack<T>();
#endregion
public ListCache()
{
Collection = new List<T>();
}
public ListCache(int capacity)
{
Collection = new List<T>(capacity);
}
/// <summary>
/// Returns T from cache when possible, or creates a new object when not.
/// </summary>
/// <returns></returns>
private T Retrieve()
{
if (_cache.Count > 0)
return _cache.Pop();
else
return Activator.CreateInstance<T>();
}
/// <summary>
/// Stores value into the cache.
/// </summary>
/// <param name="value"></param>
private void Store(T value)
{
_cache.Push(value);
}
/// <summary>
/// Adds a new value to Collection and returns it.
/// </summary>
/// <param name="value"></param>
public T AddReference()
{
T next = Retrieve();
Collection.Add(next);
return next;
}
/// <summary>
/// Inserts an bject into Collection and returns it.
/// </summary>
/// <param name="value"></param>
public T InsertReference(int index)
{
//Would just be at the end anyway.
if (index >= Collection.Count)
return AddReference();
T next = Retrieve();
Collection.Insert(index, next);
return next;
}
/// <summary>
/// Adds value to Collection.
/// </summary>
/// <param name="value"></param>
public void AddValue(T value)
{
Collection.Add(value);
}
/// <summary>
/// Inserts value into Collection.
/// </summary>
/// <param name="value"></param>
public void InsertValue(int index, T value)
{
//Would just be at the end anyway.
if (index >= Collection.Count)
AddValue(value);
else
Collection.Insert(index, value);
}
/// <summary>
/// Adds values to Collection.
/// </summary>
/// <param name="values"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddValues(ListCache<T> values)
{
int w = values.Written;
List<T> c = values.Collection;
for (int i = 0; i < w; i++)
AddValue(c[i]);
}
/// <summary>
/// Adds values to Collection.
/// </summary>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddValues(T[] values)
{
for (int i = 0; i < values.Length; i++)
AddValue(values[i]);
}
/// <summary>
/// Adds values to Collection.
/// </summary>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddValues(List<T> values)
{
for (int i = 0; i < values.Count; i++)
AddValue(values[i]);
}
/// <summary>
/// Adds values to Collection.
/// </summary>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddValues(HashSet<T> values)
{
foreach (T item in values)
AddValue(item);
}
/// <summary>
/// Adds values to Collection.
/// </summary>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddValues(ISet<T> values)
{
foreach (T item in values)
AddValue(item);
}
/// <summary>
/// Adds values to Collection.
/// </summary>
/// <param name="value"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddValues(IReadOnlyCollection<T> values)
{
foreach (T item in values)
AddValue(item);
}
/// <summary>
/// Resets cache.
/// </summary>
public void Reset()
{
foreach (T item in Collection)
Store(item);
Collection.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 488b0788adfd9ee43977abd5d0280124
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using FishNet.Managing;
using FishNet.Object;
using System;
using UnityEngine;
namespace FishNet.Utility.Performance
{
public abstract class ObjectPool : MonoBehaviour
{
/// <summary>
/// NetworkManager this ObjectPool belongs to.
/// </summary>
protected NetworkManager NetworkManager {get; private set;}
/// <summary>
/// Initializes this script for use.
/// </summary>
public virtual void InitializeOnce(NetworkManager nm)
{
NetworkManager = nm;
}
/// <summary>
/// Returns an object that has been stored using collectioNid of 0. A new object will be created if no stored objects are available.
/// </summary>
/// <param name="prefabId">PrefabId of the object to return.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
[Obsolete("Use RetrieveObject(int, ushort, bool)")] //Remove on 2024/01/01.
public abstract NetworkObject RetrieveObject(int prefabId, bool asServer);
/// <summary>
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
/// </summary>
/// <param name="prefabId">PrefabId of the object to return.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, bool asServer) => null;
/// <summary>
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
/// </summary>
/// <param name="prefabId">PrefabId of the object to return.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, Vector3 position, Quaternion rotation, bool asServer) => null;
/// <summary>
/// Stores an object into the pool.
/// </summary>
/// <param name="instantiated">Object to store.</param>
/// <param name="asServer">True if being called on the server side.</param>
/// <returns></returns>
public abstract void StoreObject(NetworkObject instantiated, bool asServer);
/// <summary>
/// Instantiates a number of objects and adds them to the pool.
/// </summary>
/// <param name="prefab">Prefab to cache.</param>
/// <param name="count">Quantity to spawn.</param>
/// <param name="asServer">True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects.</param>
public virtual void CacheObjects(NetworkObject prefab, int count, bool asServer) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ec7d855ffa7afc45b619b84ddbda27c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,339 @@
using FishNet.Documenting;
using FishNet.Managing;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Utility
{
/// <summary>
/// Writes values to a collection of a set size, overwriting old values as needed.
/// </summary>
public class RingBuffer<T>
{
#region Types.
/// <summary>
/// Custom enumerator to prevent garbage collection.
/// </summary>
[APIExclude]
public struct Enumerator : IEnumerator<T>
{
#region Public.
/// <summary>
/// Current entry in the enumerator.
/// </summary>
public T Current { get; private set; }
/// <summary>
/// Actual index of the last enumerated value.
/// </summary>
public int ActualIndex
{
get
{
int total = (_startIndex + (_read - 1));
int capacity = _rollingCollection.Capacity;
if (total >= capacity)
total -= capacity;
return total;
}
}
/// <summary>
/// Simulated index of the last enumerated value.
/// </summary>
public int SimulatedIndex => (_read - 1);
#endregion
#region Private.
/// <summary>
/// RollingCollection to use.
/// </summary>
private RingBuffer<T> _rollingCollection;
/// <summary>
/// Collection to iterate.
/// </summary>
private readonly T[] _collection;
/// <summary>
/// Number of entries read during the enumeration.
/// </summary>
private int _read;
/// <summary>
/// Start index of enumerations.
/// </summary>
private int _startIndex;
#endregion
public Enumerator(RingBuffer<T> c)
{
_read = 0;
_startIndex = 0;
_rollingCollection = c;
_collection = c.Collection;
Current = default;
}
public bool MoveNext()
{
int written = _rollingCollection.Count;
if (_read >= written)
{
ResetRead();
return false;
}
int index = (_startIndex + _read);
int capacity = _rollingCollection.Capacity;
if (index >= capacity)
index -= capacity;
Current = _collection[index];
_read++;
return true;
}
/// <summary>
/// Sets a new start index to begin reading at.
/// </summary>
public void SetStartIndex(int index)
{
_startIndex = index;
ResetRead();
}
/// <summary>
/// Sets a new start index to begin reading at.
/// </summary>
public void AddStartIndex(int value)
{
_startIndex += value;
int cap = _rollingCollection.Capacity;
if (_startIndex > cap)
_startIndex -= cap;
else if (_startIndex < 0)
_startIndex += cap;
ResetRead();
}
/// <summary>
/// Resets number of entries read during the enumeration.
/// </summary>
public void ResetRead()
{
_read = 0;
}
/// <summary>
/// Resets read count.
/// </summary>
public void Reset()
{
_startIndex = 0;
ResetRead();
}
object IEnumerator.Current => Current;
public void Dispose() { }
}
#endregion
#region Public.
/// <summary>
/// Current write index of the collection.
/// </summary>
public int WriteIndex { get; private set; }
/// <summary>
/// Number of entries currently written.
/// </summary>
public int Count => _written;
/// <summary>
/// Maximum size of the collection.
/// </summary>
public int Capacity => Collection.Length;
/// <summary>
/// Collection being used.
/// </summary>
public T[] Collection = new T[0];
/// <summary>
/// True if initialized.
/// </summary>
public bool Initialized { get; private set; }
#endregion
#region Private.
/// <summary>
/// Number of entries written. This will never go beyond the capacity but will be less until capacity is filled.
/// </summary>
private int _written;
/// <summary>
/// Enumerator for the collection.
/// </summary>
private Enumerator _enumerator;
#endregion
/// <summary>
/// Initializes the collection at length.
/// </summary>
/// <param name="capacity">Size to initialize the collection as. This cannot be changed after initialized.</param>
public void Initialize(int capacity)
{
if (capacity <= 0)
{
NetworkManager.StaticLogError($"Collection length must be larger than 0.");
return;
}
Collection = new T[capacity];
_enumerator = new Enumerator(this);
Initialized = true;
}
/// <summary>
/// Clears the collection to default values and resets indexing.
/// </summary>
public void Clear()
{
for (int i = 0; i < Collection.Length; i++)
Collection[i] = default;
Reset();
}
/// <summary>
/// Resets the collection without clearing.
/// </summary>
public void Reset()
{
_written = 0;
WriteIndex = 0;
_enumerator.Reset();
}
/// <summary>
/// Adds an entry to the collection, returning a replaced entry.
/// </summary>
/// <param name="data">Data to add.</param>
/// <returns>Replaced entry. Value will be default if no entry was replaced.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Add(T data)
{
if (!IsInitializedWithError())
return default;
int capacity = Capacity;
T current = Collection[WriteIndex];
Collection[WriteIndex] = data;
WriteIndex++;
_written++;
//If index would exceed next iteration reset it.
if (WriteIndex >= capacity)
WriteIndex = 0;
/* If written has exceeded capacity
* then the start index needs to be moved
* to adjust for overwritten values. */
if (_written > capacity)
{
_written = capacity;
_enumerator.SetStartIndex(WriteIndex);
}
return current;
}
/// <summary>
/// Returns value in actual index as it relates to simulated index.
/// </summary>
/// <param name="simulatedIndex">Simulated index to return. A value of 0 would return the first simulated index in the collection.</param>
/// <returns></returns>
public T this[int simulatedIndex]
{
get
{
int offset = (Capacity - _written) + simulatedIndex + WriteIndex;
if (offset >= Capacity)
offset -= Capacity;
return Collection[offset];
}
set
{
int offset = (Capacity - _written) + simulatedIndex + WriteIndex;
if (offset >= Capacity)
offset -= Capacity;
Collection[offset] = value;
}
}
/// <summary>
/// Returns Enumerator for the collection.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
if (!IsInitializedWithError())
return default;
_enumerator.ResetRead();
return _enumerator;
}
/// <summary>
/// Removes values from the simulated start of the collection.
/// </summary>
/// <param name="fromStart">True to remove from the start, false to remove from the end.</param>
/// <param name="length">Number of entries to remove.</param>
public void RemoveRange(bool fromStart, int length)
{
if (length == 0)
return;
if (length < 0)
{
NetworkManager.StaticLogError($"Negative values cannot be removed.");
return;
}
//Full reset if value is at or more than written.
if (length >= _written)
{
Reset();
return;
}
_written -= length;
if (fromStart)
{
_enumerator.AddStartIndex(length);
}
else
{
WriteIndex -= length;
if (WriteIndex < 0)
WriteIndex += Capacity;
}
}
/// <summary>
/// Returns if initialized and errors if not.
/// </summary>
/// <returns></returns>
private bool IsInitializedWithError()
{
if (!Initialized)
{
NetworkManager.StaticLogError($"RingBuffer has not yet been initialized.");
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a066c51d748a04546875bd7d43118837
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using FishNet.Object;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.Utility.Performance
{
public static class GetNonAlloc
{
/// <summary>
/// Gets all NetworkBehaviours on a transform.
/// </summary>
public static void GetNetworkBehavioursNonAlloc(this Transform t, ref List<NetworkBehaviour> results)
{
t.GetComponents(results);
}
/// <summary>
/// Gets all transforms on transform and it's children.
/// </summary>
public static void GetTransformsInChildrenNonAlloc(this Transform t, ref List<Transform> results, bool includeInactive = false)
{
t.GetComponentsInChildren<Transform>(includeInactive, results);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d0740f919077254c8ffb131b9587407
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: