This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs

259 lines
10 KiB
C#

using FishNet.Managing.Object;
using FishNet.Object;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityComponent = UnityEngine.Component;
namespace FishNet.Managing
{
public partial class NetworkManager : MonoBehaviour
{
#region Serialized.
/// <summary>
///
/// </summary>
[Tooltip("Collection to use for spawnable objects.")]
[SerializeField]
private PrefabObjects _spawnablePrefabs;
/// <summary>
/// Collection to use for spawnable objects.
/// </summary>
public PrefabObjects SpawnablePrefabs { get => _spawnablePrefabs; set => _spawnablePrefabs = value; }
/// <summary>
///
/// </summary>
private Dictionary<ushort, PrefabObjects> _runtimeSpawnablePrefabs = new Dictionary<ushort, PrefabObjects>();
/// <summary>
/// Collection to use for spawnable objects added at runtime, such as addressables.
/// </summary>
public IReadOnlyDictionary<ushort, PrefabObjects> RuntimeSpawnablePrefabs => _runtimeSpawnablePrefabs;
#endregion
#region Private.
/// <summary>
/// Delegates waiting to be invoked when a component is registered.
/// </summary>
private Dictionary<string, List<Action<UnityComponent>>> _pendingInvokes = new Dictionary<string, List<Action<UnityComponent>>>();
/// <summary>
/// Currently registered components.
/// </summary>
private Dictionary<string, UnityComponent> _registeredComponents = new Dictionary<string, UnityComponent>();
#endregion
/// <summary>
/// Gets the PrefabObjects to use for spawnableCollectionId.
/// </summary>
/// <typeparam name="T">Type of PrefabObjects to return. This is also used to create an instance of type when createIfMissing is true.</typeparam>
/// <param name="spawnableCollectionId">Id to use. 0 will return the configured SpawnablePrefabs.</param>
/// <param name="createIfMissing">True to create and assign a PrefabObjects if missing for the collectionId.</param>
/// <returns></returns>
public PrefabObjects GetPrefabObjects<T>(ushort spawnableCollectionId, bool createIfMissing) where T : PrefabObjects
{
if (spawnableCollectionId == 0)
{
if (createIfMissing)
{
LogError($"SpawnableCollectionId cannot be 0 when create missing is true.");
return null;
}
else
{
return SpawnablePrefabs;
}
}
PrefabObjects po;
if (!_runtimeSpawnablePrefabs.TryGetValue(spawnableCollectionId, out po))
{
//Do not create missing, return null for not found.
if (!createIfMissing)
return null;
po = ScriptableObject.CreateInstance<T>();
po.SetCollectionId(spawnableCollectionId);
_runtimeSpawnablePrefabs[spawnableCollectionId] = po;
}
return po;
}
/// <summary>
/// Removes the PrefabObjects collection from memory.
/// This should only be called after you properly disposed of it's contents properly.
/// </summary>
/// <param name="spawnableCollectionId">CollectionId to remove.</param>
/// <returns>True if collection was found and removed.</returns>
public bool RemoveSpawnableCollection(ushort spawnableCollectionId)
{
return _runtimeSpawnablePrefabs.Remove(spawnableCollectionId);
}
/// <summary>
/// Gets the index a prefab uses. Can be used in conjuction with GetPrefab.
/// </summary>
/// <param name="prefab"></param>
/// <param name="asServer">True if to get from the server collection.</param>
/// <returns>Returns index if found, and -1 if not found.</returns>
public int GetPrefabIndex(GameObject prefab, bool asServer)
{
int count = SpawnablePrefabs.GetObjectCount();
for (int i = 0; i < count; i++)
{
GameObject go = SpawnablePrefabs.GetObject(asServer, i).gameObject;
if (go == prefab)
return i;
}
//Fall through, not found.
return -1;
}
/// <summary>
/// Returns a prefab with prefabId.
/// This method will bypass object pooling.
/// </summary>
/// <param name="prefabId">PrefabId to get.</param>
/// <param name="asServer">True if getting the prefab asServer.</param>
public NetworkObject GetPrefab(int prefabId, bool asServer)
{
return SpawnablePrefabs.GetObject(asServer, prefabId);
}
#region Registered components
/// <summary>
/// Invokes an action when a specified component becomes registered. Action will invoke immediately if already registered.
/// </summary>
/// <typeparam name="T">Component type.</typeparam>
/// <param name="handler">Action to invoke.</param>
public void RegisterInvokeOnInstance<T>(Action<UnityComponent> handler) where T : UnityComponent
{
T result = GetInstance<T>(false);
//If not found yet make a pending invoke.
if (result == default(T))
{
string tName = GetInstanceName<T>();
List<Action<UnityComponent>> handlers;
if (!_pendingInvokes.TryGetValue(tName, out handlers))
{
handlers = new List<Action<UnityComponent>>();
_pendingInvokes[tName] = handlers;
}
handlers.Add(handler);
}
//Already exist, invoke right away.
else
{
handler.Invoke(result);
}
}
/// <summary>
/// Removes an action to be invokes when a specified component becomes registered.
/// </summary>
/// <typeparam name="T">Component type.</typeparam>
/// <param name="handler">Action to invoke.</param>
public void UnregisterInvokeOnInstance<T>(Action<UnityComponent> handler) where T : UnityComponent
{
string tName = GetInstanceName<T>();
List<Action<UnityComponent>> handlers;
if (!_pendingInvokes.TryGetValue(tName, out handlers))
return;
handlers.Remove(handler);
//Do not remove pending to prevent garbage collection later on list recreation.
}
/// <summary>
/// Returns if an instance exists for type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public bool HasInstance<T>() where T : UnityComponent
{
return (GetInstance<T>(false) != null);
}
/// <summary>
/// Returns class of type if found within CodegenBase classes.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="warn">True to warn if component is not registered.</param>
/// <returns></returns>
public T GetInstance<T>(bool warn = true) where T : UnityComponent
{
string tName = GetInstanceName<T>();
if (_registeredComponents.TryGetValue(tName, out UnityComponent result))
return (T)result;
else if (warn)
LogWarning($"Component {tName} is not registered.");
return default(T);
}
/// <summary>
/// Registers a new component to this NetworkManager.
/// </summary>
/// <typeparam name="T">Type to register.</typeparam>
/// <param name="component">Reference of the component being registered.</param>
/// <param name="replace">True to replace existing references.</param>
public void RegisterInstance<T>(T component, bool replace = true) where T : UnityComponent
{
string tName = GetInstanceName<T>();
if (_registeredComponents.ContainsKey(tName) && !replace)
{
LogWarning($"Component {tName} is already registered.");
}
else
{
_registeredComponents[tName] = component;
RemoveNullPendingDelegates();
//If in pending invokes also send these out.
if (_pendingInvokes.TryGetValue(tName, out List<Action<UnityComponent>> dels))
{
for (int i = 0; i < dels.Count; i++)
dels[i].Invoke(component);
/* Clear delegates but do not remove dictionary entry
* to prevent list from being re-initialized. */
dels.Clear();
}
}
}
/// <summary>
/// Unregisters a component from this NetworkManager.
/// </summary>
/// <typeparam name="T">Type to unregister.</typeparam>
public void UnregisterInstance<T>() where T : UnityComponent
{
string tName = GetInstanceName<T>();
_registeredComponents.Remove(tName);
}
/// <summary>
/// Removes delegates from pending invokes when may have gone missing.
/// </summary>
private void RemoveNullPendingDelegates()
{
foreach (List<Action<UnityComponent>> handlers in _pendingInvokes.Values)
{
for (int i = 0; i < handlers.Count; i++)
{
if (handlers[i] == null)
{
handlers.RemoveAt(i);
i--;
}
}
}
}
/// <summary>
/// Returns the name to use for T.
/// </summary>
private string GetInstanceName<T>()
{
return typeof(T).FullName;
}
#endregion
}
}