forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			315 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using FishNet.Connection; //remove on 2023/01/01 move to correct folder.
 | 
						|
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)]
 | 
						|
namespace FishNet.Managing.Observing
 | 
						|
{
 | 
						|
    /// <summary>
 | 
						|
    /// Additional options for managing the observer system.
 | 
						|
    /// </summary>
 | 
						|
    [DisallowMultipleComponent]
 | 
						|
    [AddComponentMenu("FishNet/Manager/ObserverManager")]
 | 
						|
    public sealed class ObserverManager : MonoBehaviour
 | 
						|
    {
 | 
						|
        #region Internal.
 | 
						|
        /// <summary>
 | 
						|
        /// Current index to use for level of detail based on tick.
 | 
						|
        /// </summary>
 | 
						|
        internal byte LevelOfDetailIndex { get; private set; }
 | 
						|
        #endregion
 | 
						|
 | 
						|
        #region Serialized.
 | 
						|
        /// <summary>
 | 
						|
        /// 
 | 
						|
        /// </summary>
 | 
						|
        [Tooltip("True to use the NetworkLOD system.")]
 | 
						|
        [SerializeField]
 | 
						|
        private bool _useNetworkLod;
 | 
						|
        /// <summary>
 | 
						|
        /// True to use the NetworkLOD system.
 | 
						|
        /// </summary>
 | 
						|
        /// <returns></returns>
 | 
						|
        internal bool GetUseNetworkLod() => _useNetworkLod;
 | 
						|
        /// <summary>
 | 
						|
        /// Distance for each level of detal.
 | 
						|
        /// </summary>
 | 
						|
        internal List<float> GetLevelOfDetailDistances() => (_useNetworkLod) ? _levelOfDetailDistances : _singleLevelOfDetailDistances;
 | 
						|
        [Tooltip("Distance for each level of detal.")]
 | 
						|
        [SerializeField]
 | 
						|
        private List<float> _levelOfDetailDistances = new List<float>();
 | 
						|
        /// <summary>
 | 
						|
        /// Returned when network LOD is off. Value contained is one level of detail with max distance.
 | 
						|
        /// </summary>
 | 
						|
        private List<float> _singleLevelOfDetailDistances = new List<float>() { float.MaxValue };
 | 
						|
        /// <summary>
 | 
						|
        /// 
 | 
						|
        /// </summary>
 | 
						|
        [Tooltip("True to update visibility for clientHost based on if they are an observer or not.")]
 | 
						|
        [FormerlySerializedAs("_setHostVisibility")]
 | 
						|
        [SerializeField]
 | 
						|
        private bool _updateHostVisibility = true;
 | 
						|
        /// <summary>
 | 
						|
        /// True to update visibility for clientHost based on if they are an observer or not.
 | 
						|
        /// </summary>
 | 
						|
        public bool UpdateHostVisibility
 | 
						|
        {
 | 
						|
            get => _updateHostVisibility;
 | 
						|
            private set => _updateHostVisibility = value;
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// 
 | 
						|
        /// </summary>
 | 
						|
        [Tooltip("Default observer conditions for networked objects.")]
 | 
						|
        [SerializeField]
 | 
						|
        private List<ObserverCondition> _defaultConditions = new List<ObserverCondition>();
 | 
						|
        #endregion
 | 
						|
 | 
						|
        #region Private.
 | 
						|
        /// <summary>
 | 
						|
        /// NetworkManager on object.
 | 
						|
        /// </summary>
 | 
						|
        private NetworkManager _networkManager;
 | 
						|
        /// <summary>
 | 
						|
        /// Intervals for each level of detail.
 | 
						|
        /// </summary>
 | 
						|
        private uint[] _levelOfDetailIntervals;
 | 
						|
        #endregion
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Initializes this script for use.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="manager"></param>
 | 
						|
        internal void InitializeOnce_Internal(NetworkManager manager)
 | 
						|
        {
 | 
						|
            _networkManager = manager;
 | 
						|
            ValidateLevelOfDetails();
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Sets a new value for UpdateHostVisibility.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="value">New value.</param>
 | 
						|
        /// <param name="updateType">Which objects to update.</param>
 | 
						|
        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;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Adds default observer conditions to nob and returns the NetworkObserver used.
 | 
						|
        /// </summary>
 | 
						|
        internal NetworkObserver AddDefaultConditions(NetworkObject nob)
 | 
						|
        {
 | 
						|
            bool isGlobal = (nob.IsGlobal && !nob.IsSceneObject);
 | 
						|
            bool obsAdded;
 | 
						|
 | 
						|
            NetworkObserver result;
 | 
						|
            if (!nob.TryGetComponent<NetworkObserver>(out result))
 | 
						|
            {
 | 
						|
                obsAdded = true;
 | 
						|
                result = nob.gameObject.AddComponent<NetworkObserver>();
 | 
						|
            }
 | 
						|
            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;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Gets the tick interval to use for a lod level.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="lodLevel"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        public byte GetLevelOfDetailInterval(byte lodLevel)
 | 
						|
        {
 | 
						|
            if (LevelOfDetailIndex == 0)
 | 
						|
                return 1;
 | 
						|
 | 
						|
            return (byte)System.Math.Pow(2, lodLevel);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Calculates and sets the current level of detail index for the tick.
 | 
						|
        /// </summary>
 | 
						|
        internal void CalculateLevelOfDetail(uint tick)
 | 
						|
        {
 | 
						|
            int count = GetLevelOfDetailDistances().Count;
 | 
						|
            for (int i = (count - 1); i > 0; i--)
 | 
						|
            {
 | 
						|
                uint interval = _levelOfDetailIntervals[i];
 | 
						|
                if (tick % interval == 0)
 | 
						|
                {
 | 
						|
                    LevelOfDetailIndex = (byte)i;
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //If here then index is 0 and interval is every tick.
 | 
						|
            LevelOfDetailIndex = 0;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Validates that level of detail intervals are proper.
 | 
						|
        /// </summary>
 | 
						|
        private void ValidateLevelOfDetails()
 | 
						|
        {
 | 
						|
            if (!_useNetworkLod)
 | 
						|
                return;
 | 
						|
 | 
						|
            //No distances specified.
 | 
						|
            if (_levelOfDetailDistances == null || _levelOfDetailDistances.Count == 0)
 | 
						|
            {
 | 
						|
                if (_networkManager != null)
 | 
						|
                {
 | 
						|
                    _networkManager.LogWarning("Level of detail distances contains no entries. NetworkLOD has been disabled.");
 | 
						|
                    _useNetworkLod = false;
 | 
						|
                }
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            //Make sure every distance is larger than the last.
 | 
						|
            float lastDistance = float.MinValue;
 | 
						|
            foreach (float dist in _levelOfDetailDistances)
 | 
						|
            {
 | 
						|
                if (dist <= 0f || dist <= lastDistance)
 | 
						|
                {
 | 
						|
                    if (_networkManager != null)
 | 
						|
                    {
 | 
						|
                        _networkManager.LogError($"Level of detail distances must be greater than 0f, and each distance larger than the previous. NetworkLOD has been disabled.");
 | 
						|
                        _useNetworkLod = false;
 | 
						|
                    }
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                lastDistance = dist;
 | 
						|
            }
 | 
						|
 | 
						|
            int maxEntries = 8;
 | 
						|
            //Too many distances.
 | 
						|
            if (_levelOfDetailDistances.Count > maxEntries)
 | 
						|
            {
 | 
						|
                _networkManager?.LogWarning("There can be a maximum of 8 level of detail distances. Entries beyond this quantity have been discarded.");
 | 
						|
                while (_levelOfDetailDistances.Count > maxEntries)
 | 
						|
                    _levelOfDetailDistances.RemoveAt(_levelOfDetailDistances.Count - 1);
 | 
						|
            }
 | 
						|
 | 
						|
            if (Application.isPlaying)
 | 
						|
            {
 | 
						|
                //Build intervals and sqr distances.
 | 
						|
                int count = _levelOfDetailDistances.Count;
 | 
						|
                _levelOfDetailIntervals = new uint[count];
 | 
						|
                for (int i = (count - 1); i > 0; i--)
 | 
						|
                {
 | 
						|
                    uint power = (uint)Mathf.Pow(2, i);
 | 
						|
                    _levelOfDetailIntervals[i] = power;
 | 
						|
 | 
						|
                }
 | 
						|
                //Sqr
 | 
						|
                for (int i = 0; i < count; i++)
 | 
						|
                {
 | 
						|
                    float dist = _levelOfDetailDistances[i];
 | 
						|
                    dist *= dist;
 | 
						|
                    _levelOfDetailDistances[i] = dist;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
} |