using FishNet.Managing.Object;
using FishNet.Object;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityComponent = UnityEngine.Component;
namespace FishNet.Managing
{
public partial class NetworkManager : MonoBehaviour
{
#region Public.
#region Obsoletes
[Obsolete("Use IsClientOnlyStarted. Note the difference between IsClientOnlyInitialized and IsClientOnlyStarted.")]
public bool IsClientOnly => IsClientOnlyStarted;
[Obsolete("Use IsServerOnlyStarted. Note the difference between IsServerOnlyInitialized and IsServerOnlyStarted.")]
public bool IsServerOnly => IsServerOnlyStarted;
[Obsolete("Use IsHostStarted. Note the difference between IsHostInitialized and IsHostStarted.")]
public bool IsHost => IsHostStarted;
[Obsolete("Use IsClientStarted. Note the difference between IsClientInitialized and IsClientStarted.")]
public bool IsClient => IsClientStarted;
[Obsolete("Use IsServerStarted. Note the difference between IsServerInitialized and IsServerStarted.")]
public bool IsServer => IsServerStarted;
#endregion
///
/// True if server is started.
///
public bool IsServerStarted => ServerManager.Started;
///
/// True if only the server is started.
///
public bool IsServerOnlyStarted => (IsServerStarted && !IsClientStarted);
///
/// True if the client is authenticated.
///
public bool IsClientStarted => (ClientManager.Started && ClientManager.Connection.IsAuthenticated);
///
/// True if only the client is authenticated.
///
public bool IsClientOnlyStarted => (!IsServerStarted && IsClientStarted);
///
/// True if client is authenticated, and the server is started.
///
public bool IsHostStarted => (IsServerStarted && IsClientStarted);
///
/// True if client nor server are started.
///
public bool IsOffline => (!IsServerStarted && !IsClientStarted);
#endregion
#region Serialized.
///
///
///
[Tooltip("Collection to use for spawnable objects.")]
[SerializeField]
private PrefabObjects _spawnablePrefabs;
///
/// Collection to use for spawnable objects.
///
public PrefabObjects SpawnablePrefabs { get => _spawnablePrefabs; set => _spawnablePrefabs = value; }
///
///
///
private Dictionary _runtimeSpawnablePrefabs = new();
///
/// Collection to use for spawnable objects added at runtime, such as addressables.
///
public IReadOnlyDictionary RuntimeSpawnablePrefabs => _runtimeSpawnablePrefabs;
#endregion
#region Private.
///
/// Delegates waiting to be invoked when a component is registered.
///
private Dictionary>> _pendingInvokes = new();
///
/// Currently registered components.
///
private Dictionary _registeredComponents = new();
#endregion
///
/// Gets the PrefabObjects to use for spawnableCollectionId.
///
/// Type of PrefabObjects to return. This is also used to create an instance of type when createIfMissing is true.
/// Id to use. 0 will return the configured SpawnablePrefabs.
/// True to create and assign a PrefabObjects if missing for the collectionId.
///
public PrefabObjects GetPrefabObjects(ushort spawnableCollectionId, bool createIfMissing) where T : PrefabObjects
{
if (spawnableCollectionId == 0)
{
if (createIfMissing)
{
InternalLogError($"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();
po.SetCollectionId(spawnableCollectionId);
_runtimeSpawnablePrefabs[spawnableCollectionId] = po;
}
return po;
}
///
/// Removes the PrefabObjects collection from memory.
/// This should only be called after you properly disposed of it's contents properly.
///
/// CollectionId to remove.
/// True if collection was found and removed.
public bool RemoveSpawnableCollection(ushort spawnableCollectionId)
{
return _runtimeSpawnablePrefabs.Remove(spawnableCollectionId);
}
///
/// Gets the index a prefab uses. Can be used in conjuction with GetPrefab.
///
///
/// True if to get from the server collection.
/// Returns index if found, and -1 if not found.
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;
}
///
/// Returns a prefab with prefabId.
/// This method will bypass object pooling.
///
/// PrefabId to get.
/// True if getting the prefab asServer.
public NetworkObject GetPrefab(int prefabId, bool asServer)
{
return SpawnablePrefabs.GetObject(asServer, prefabId);
}
#region Registered components
///
/// Invokes an action when a specified component becomes registered. Action will invoke immediately if already registered.
///
/// Component type.
/// Action to invoke.
public void RegisterInvokeOnInstance(Action handler) where T : UnityComponent
{
T result;
//If not found yet make a pending invoke.
if (!TryGetInstance(out result))
{
string tName = GetInstanceName();
List> handlers;
if (!_pendingInvokes.TryGetValue(tName, out handlers))
{
handlers = new();
_pendingInvokes[tName] = handlers;
}
handlers.Add(handler);
}
//Already exist, invoke right away.
else
{
handler.Invoke(result);
}
}
///
/// Removes an action to be invokes when a specified component becomes registered.
///
/// Component type.
/// Action to invoke.
public void UnregisterInvokeOnInstance(Action handler) where T : UnityComponent
{
string tName = GetInstanceName();
List> handlers;
if (!_pendingInvokes.TryGetValue(tName, out handlers))
return;
handlers.Remove(handler);
//Do not remove pending to prevent garbage collection later from recreation.
}
///
/// Returns if an instance exists for type.
///
/// Type to check.
///
public bool HasInstance() where T : UnityComponent
{
return TryGetInstance(out _);
}
///
/// Returns class of type from registered instances.
/// A warning will display if not found.
///
/// Type to get.
///
public T GetInstance() where T : UnityComponent
{
T result;
if (TryGetInstance(out result))
return result;
else
InternalLogWarning($"Component {GetInstanceName()} is not registered. To avoid this warning use TryGetInstance(T).");
return default(T);
}
///
/// Returns class of type from registered instances.
///
/// Outputted component.
/// Type to get.
/// True if was able to get instance.
public bool TryGetInstance(out T result) where T : UnityComponent
{
string tName = GetInstanceName();
if (_registeredComponents.TryGetValue(tName, out UnityComponent v))
{
result = (T)v;
return true;
}
else
{
result = default;
return false;
}
}
///
/// Registers a new component to this NetworkManager.
///
/// Type to register.
/// Reference of the component being registered.
/// True to replace existing references.
public void RegisterInstance(T component, bool replace = true) where T : UnityComponent
{
string tName = GetInstanceName();
if (_registeredComponents.ContainsKey(tName) && !replace)
{
InternalLogWarning($"Component {tName} is already registered.");
}
else
{
_registeredComponents[tName] = component;
RemoveNullPendingDelegates();
//If in pending invokes also send these out.
if (_pendingInvokes.TryGetValue(tName, out List> 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();
}
}
}
///
/// Tries to registers a new component to this NetworkManager.
/// This will not register the instance if another already exists.
///
/// Type to register.
/// Reference of the component being registered.
/// True if was able to register, false if an instance is already registered.
public bool TryRegisterInstance(T component) where T : UnityComponent
{
string tName = GetInstanceName();
if (_registeredComponents.ContainsKey(tName))
return false;
else
RegisterInstance(component, false);
return true;
}
///
/// Unregisters a component from this NetworkManager.
///
/// Type to unregister.
public void UnregisterInstance() where T : UnityComponent
{
string tName = GetInstanceName();
_registeredComponents.Remove(tName);
}
///
/// Removes delegates from pending invokes when may have gone missing.
///
private void RemoveNullPendingDelegates()
{
foreach (List> handlers in _pendingInvokes.Values)
{
for (int i = 0; i < handlers.Count; i++)
{
if (handlers[i] == null)
{
handlers.RemoveAt(i);
i--;
}
}
}
}
///
/// Returns the name to use for T.
///
private string GetInstanceName()
{
return typeof(T).FullName;
}
#endregion
}
}