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