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. /// /// /// [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 Dictionary(); /// /// 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 Dictionary>>(); /// /// Currently registered components. /// private Dictionary _registeredComponents = new Dictionary(); #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) { 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(); 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 = GetInstance(false); //If not found yet make a pending invoke. if (result == default(T)) { string tName = GetInstanceName(); List> handlers; if (!_pendingInvokes.TryGetValue(tName, out handlers)) { handlers = new List>(); _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 on list recreation. } /// /// Returns if an instance exists for type. /// /// /// public bool HasInstance() where T : UnityComponent { return (GetInstance(false) != null); } /// /// Returns class of type if found within CodegenBase classes. /// /// /// True to warn if component is not registered. /// public T GetInstance(bool warn = true) where T : UnityComponent { string tName = GetInstanceName(); if (_registeredComponents.TryGetValue(tName, out UnityComponent result)) return (T)result; else if (warn) LogWarning($"Component {tName} is not registered."); return default(T); } /// /// 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) { 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> 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(); } } } /// /// 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 } }