using FishNet.Component.Observing; using FishNet.Connection; using FishNet.Object; using FishNet.Observing; using FishNet.Utility.Constant; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; using UnityEngine.Serialization; [assembly: InternalsVisibleTo(UtilityConstants.DEMOS_ASSEMBLY_NAME)] [assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)] namespace FishNet.Managing.Observing { /// /// Additional options for managing the observer system. /// [DisallowMultipleComponent] [AddComponentMenu("FishNet/Manager/ObserverManager")] public sealed class ObserverManager : MonoBehaviour { #region Internal. /// /// Current index to use for level of detail based on tick. /// internal byte LevelOfDetailIndex { get; private set; } #endregion #region Serialized. /// /// /// [Tooltip("True to use the NetworkLOD system.")] [FormerlySerializedAs("_useNetworkLod")]//Remove on 2024/01/01 [SerializeField] private bool _enableNetworkLod; /// /// True to use the NetworkLOD system. /// /// internal bool GetEnableNetworkLod() => _enableNetworkLod; /// /// Distance for each level of detal. /// internal List GetLevelOfDetailDistances() => (_enableNetworkLod) ? _levelOfDetailDistances : _singleLevelOfDetailDistances; [Tooltip("Distance for each level of detal.")] [SerializeField] private List _levelOfDetailDistances = new List(); /// /// Returned when network LOD is off. Value contained is one level of detail with max distance. /// private List _singleLevelOfDetailDistances = new List() { float.MaxValue }; /// /// /// [Tooltip("True to update visibility for clientHost based on if they are an observer or not.")] [FormerlySerializedAs("_setHostVisibility")]//Remove on 2024/01/01 [SerializeField] private bool _updateHostVisibility = true; /// /// True to update visibility for clientHost based on if they are an observer or not. /// public bool UpdateHostVisibility { get => _updateHostVisibility; private set => _updateHostVisibility = value; } /// /// /// [Tooltip("Default observer conditions for networked objects.")] [SerializeField] private List _defaultConditions = new List(); #endregion #region Private. /// /// NetworkManager on object. /// private NetworkManager _networkManager; /// /// Intervals for each level of detail. /// private uint[] _levelOfDetailIntervals; #endregion /// /// Initializes this script for use. /// /// internal void InitializeOnce_Internal(NetworkManager manager) { _networkManager = manager; ValidateLevelOfDetails(); } /// /// Sets a new value for UpdateHostVisibility. /// /// New value. /// Which objects to update. public void SetUpdateHostVisibility(bool value, HostVisibilityUpdateTypes updateType) { //Unchanged. if (value == UpdateHostVisibility) return; /* Update even if server state is not known. * The setting should be updated so when the server * does start spawned objects have latest setting. */ if (HostVisibilityUpdateContains(updateType, HostVisibilityUpdateTypes.Manager)) UpdateHostVisibility = value; /* If to update spawned as well then update all networkobservers * with the setting and also update renderers. */ if (_networkManager.IsServer && HostVisibilityUpdateContains(updateType, HostVisibilityUpdateTypes.Spawned)) { NetworkConnection clientConn = _networkManager.ClientManager.Connection; foreach (NetworkObject n in _networkManager.ServerManager.Objects.Spawned.Values) { n.NetworkObserver.SetUpdateHostVisibility(value); //Only check to update renderers if clientHost. If not client then clientConn won't be active. if (clientConn.IsActive) n.SetRenderersVisible(n.Observers.Contains(clientConn), true); } } bool HostVisibilityUpdateContains(HostVisibilityUpdateTypes whole, HostVisibilityUpdateTypes part) { return (whole & part) == part; } } /// /// Adds default observer conditions to nob and returns the NetworkObserver used. /// internal NetworkObserver AddDefaultConditions(NetworkObject nob) { bool isGlobal = (nob.IsGlobal && !nob.IsSceneObject); bool obsAdded; NetworkObserver result; if (!nob.TryGetComponent(out result)) { obsAdded = true; result = nob.gameObject.AddComponent(); } else { obsAdded = false; } /* NetworkObserver is null and there are no * conditions to add. Nothing will change by adding * the NetworkObserver component so exit early. */ if (!obsAdded && _defaultConditions.Count == 0) return result; //If the NetworkObserver component was just added. if (obsAdded) { /* Global nobs do not need a NetworkObserver. * Ultimately, a global NetworkObject is one without * any conditions. */ if (isGlobal) return result; //If there are no conditions then there's nothing to add. if (_defaultConditions.Count == 0) return result; /* If here then there not a global networkobject and there are conditions to use. * Since the NetworkObserver is being added fresh, set OverrideType to UseManager * so that the NetworkObserver is populated with the manager conditions. */ result.OverrideType = NetworkObserver.ConditionOverrideType.UseManager; } //NetworkObject has a NetworkObserver already on it. else { //If global the NetworkObserver has to be cleared and set to ignore manager. if (isGlobal) { result.ObserverConditionsInternal.Clear(); result.OverrideType = NetworkObserver.ConditionOverrideType.IgnoreManager; } } //If ignoring manager then use whatever is already configured. if (result.OverrideType == NetworkObserver.ConditionOverrideType.IgnoreManager) { //Do nothing. } //If using manager then replace all with conditions. else if (result.OverrideType == NetworkObserver.ConditionOverrideType.UseManager) { result.ObserverConditionsInternal.Clear(); AddMissing(result); } //Adding only new. else if (result.OverrideType == NetworkObserver.ConditionOverrideType.AddMissing) { AddMissing(result); } void AddMissing(NetworkObserver networkObserver) { int count = _defaultConditions.Count; for (int i = 0; i < count; i++) { ObserverCondition oc = _defaultConditions[i]; if (!networkObserver.ObserverConditionsInternal.Contains(oc)) networkObserver.ObserverConditionsInternal.Add(oc); } } return result; } /// /// Gets the tick interval to use for a lod level. /// /// /// public static byte GetLevelOfDetailInterval(byte lodIndex) { //Minimum of 1 is required. if (lodIndex == 0) return 1; return (byte)System.Math.Pow(2, lodIndex); } /// /// Calculates and sets the current level of detail index for the tick. /// internal void CalculateLevelOfDetail(uint tick) { //If here then index is 0 and interval is every tick. LevelOfDetailIndex = 0; } /// /// Validates that level of detail intervals are proper. /// private void ValidateLevelOfDetails() { #if !FISHNET_PRO _enableNetworkLod = false; #endif } } }