forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			405 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using FishNet.Connection;
 | 
						|
using FishNet.Documenting;
 | 
						|
using FishNet.Object;
 | 
						|
using FishNet.Transporting;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Runtime.CompilerServices;
 | 
						|
using UnityEngine;
 | 
						|
using UnityEngine.Serialization;
 | 
						|
 | 
						|
namespace FishNet.Observing
 | 
						|
{
 | 
						|
    /// <summary>
 | 
						|
    /// Controls which clients can see and get messages for an object.
 | 
						|
    /// </summary>
 | 
						|
    [DisallowMultipleComponent]
 | 
						|
    [AddComponentMenu("FishNet/Component/NetworkObserver")]
 | 
						|
    public sealed class NetworkObserver : MonoBehaviour
 | 
						|
    {
 | 
						|
        #region Types.
 | 
						|
        /// <summary>
 | 
						|
        /// How ObserverManager conditions are used.
 | 
						|
        /// </summary>
 | 
						|
        public enum ConditionOverrideType
 | 
						|
        {
 | 
						|
            /// <summary>
 | 
						|
            /// Keep current conditions, add new conditions from manager.
 | 
						|
            /// </summary>
 | 
						|
            AddMissing = 1,
 | 
						|
            /// <summary>
 | 
						|
            /// Replace current conditions with manager conditions.
 | 
						|
            /// </summary>
 | 
						|
            UseManager = 2,
 | 
						|
            /// <summary>
 | 
						|
            /// Keep current conditions, ignore manager conditions.
 | 
						|
            /// </summary>
 | 
						|
            IgnoreManager = 3,
 | 
						|
        }
 | 
						|
        #endregion
 | 
						|
 | 
						|
        #region Serialized.
 | 
						|
        /// <summary>
 | 
						|
        /// 
 | 
						|
        /// </summary>
 | 
						|
        [Tooltip("How ObserverManager conditions are used.")]
 | 
						|
        [SerializeField]
 | 
						|
        private ConditionOverrideType _overrideType = ConditionOverrideType.IgnoreManager;
 | 
						|
        /// <summary>
 | 
						|
        /// How ObserverManager conditions are used.
 | 
						|
        /// </summary>
 | 
						|
        public ConditionOverrideType OverrideType
 | 
						|
        {
 | 
						|
            get => _overrideType;
 | 
						|
            internal set => _overrideType = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <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("Conditions connections must met to be added as an observer. Multiple conditions may be used.")]
 | 
						|
        [SerializeField]
 | 
						|
        internal List<ObserverCondition> _observerConditions = new List<ObserverCondition>();
 | 
						|
        /// <summary>
 | 
						|
        /// Conditions connections must met to be added as an observer. Multiple conditions may be used.
 | 
						|
        /// </summary>
 | 
						|
        public IReadOnlyList<ObserverCondition> ObserverConditions => _observerConditions;
 | 
						|
        [APIExclude]
 | 
						|
#if MIRROR
 | 
						|
        public List<ObserverCondition> ObserverConditionsInternal
 | 
						|
#else
 | 
						|
        internal List<ObserverCondition> ObserverConditionsInternal
 | 
						|
#endif
 | 
						|
        {
 | 
						|
            get => _observerConditions;
 | 
						|
            set => _observerConditions = value;
 | 
						|
        }
 | 
						|
        #endregion
 | 
						|
 | 
						|
        #region Private.
 | 
						|
        /// <summary>
 | 
						|
        /// Conditions under this component which are timed.
 | 
						|
        /// </summary>
 | 
						|
        private List<ObserverCondition> _timedConditions = new List<ObserverCondition>();
 | 
						|
        /// <summary>
 | 
						|
        /// Connections which have all non-timed conditions met.
 | 
						|
        /// </summary>
 | 
						|
        private HashSet<NetworkConnection> _nonTimedMet = new HashSet<NetworkConnection>();
 | 
						|
        /// <summary>
 | 
						|
        /// NetworkObject this belongs to.
 | 
						|
        /// </summary>
 | 
						|
        private NetworkObject _networkObject;
 | 
						|
        /// <summary>
 | 
						|
        /// Becomes true when registered with ServerObjects as Timed observers.
 | 
						|
        /// </summary>
 | 
						|
        private bool _registeredAsTimed;
 | 
						|
        /// <summary>
 | 
						|
        /// True if already pre-initialized.
 | 
						|
        /// </summary>
 | 
						|
        private bool _preintiialized;
 | 
						|
        /// <summary>
 | 
						|
        /// True if ParentNetworkObject was visible last iteration.
 | 
						|
        /// This value will also be true if there is no ParentNetworkObject.
 | 
						|
        /// </summary>
 | 
						|
        private bool _lastParentVisible;
 | 
						|
        #endregion
 | 
						|
 | 
						|
        private void OnEnable()
 | 
						|
        {
 | 
						|
            if (_networkObject != null && _networkObject.IsServer)
 | 
						|
                RegisterTimedConditions();
 | 
						|
        }
 | 
						|
        private void OnDisable()
 | 
						|
        {
 | 
						|
            if (_networkObject != null && _networkObject.IsDeinitializing)
 | 
						|
            {
 | 
						|
                _lastParentVisible = false;
 | 
						|
                _nonTimedMet.Clear();
 | 
						|
                UnregisterTimedConditions();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        private void OnDestroy()
 | 
						|
        {
 | 
						|
            if (_networkObject != null)
 | 
						|
                UnregisterTimedConditions();
 | 
						|
        }
 | 
						|
 | 
						|
        internal void Deinitialize()
 | 
						|
        {
 | 
						|
            if (_networkObject != null && _networkObject.IsDeinitializing)
 | 
						|
            {
 | 
						|
                _networkObject.ServerManager.OnRemoteConnectionState -= ServerManager_OnRemoteConnectionState;
 | 
						|
                UnregisterTimedConditions();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Initializes this script for use.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="networkManager"></param>
 | 
						|
        internal void PreInitialize(NetworkObject networkObject)
 | 
						|
        {
 | 
						|
            if (!_preintiialized)
 | 
						|
            {
 | 
						|
                _preintiialized = true;
 | 
						|
                _networkObject = networkObject;
 | 
						|
                bool ignoringManager = (OverrideType == ConditionOverrideType.IgnoreManager);
 | 
						|
 | 
						|
                //Check to override SetHostVisibility.
 | 
						|
                if (!ignoringManager)
 | 
						|
                    UpdateHostVisibility = networkObject.ObserverManager.UpdateHostVisibility;
 | 
						|
 | 
						|
                bool observerFound = false;
 | 
						|
                for (int i = 0; i < _observerConditions.Count; i++)
 | 
						|
                {
 | 
						|
                    if (_observerConditions[i] != null)
 | 
						|
                    {
 | 
						|
                        observerFound = true;
 | 
						|
 | 
						|
                        /* Make an instance of each condition so values are
 | 
						|
                         * not overwritten when the condition exist more than
 | 
						|
                         * once in the scene. Double edged sword of using scriptable
 | 
						|
                         * objects for conditions. */
 | 
						|
                        _observerConditions[i] = _observerConditions[i].Clone();
 | 
						|
                        ObserverCondition oc = _observerConditions[i];
 | 
						|
                        oc.InitializeOnce(_networkObject);
 | 
						|
                        //If timed also register as containing timed conditions.
 | 
						|
                        if (oc.Timed())
 | 
						|
                            _timedConditions.Add(oc);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        _observerConditions.RemoveAt(i);
 | 
						|
                        i--;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                //No observers specified, do not need to take further action.
 | 
						|
                if (!observerFound)
 | 
						|
                    return;
 | 
						|
 | 
						|
                _networkObject.ServerManager.OnRemoteConnectionState += ServerManager_OnRemoteConnectionState;
 | 
						|
            }
 | 
						|
 | 
						|
            RegisterTimedConditions();
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Returns a condition if found within Conditions.
 | 
						|
        /// </summary>
 | 
						|
        /// <returns></returns>
 | 
						|
        public ObserverCondition GetObserverCondition<T>() where T : ObserverCondition
 | 
						|
        {
 | 
						|
            /* Do not bother setting local variables,
 | 
						|
             * condition collections aren't going to be long
 | 
						|
             * enough to make doing so worth while. */
 | 
						|
 | 
						|
            System.Type conditionType = typeof(T);
 | 
						|
            for (int i = 0; i < _observerConditions.Count; i++)
 | 
						|
            {
 | 
						|
                if (_observerConditions[i].GetType() == conditionType)
 | 
						|
                    return _observerConditions[i];
 | 
						|
            }
 | 
						|
 | 
						|
            //Fall through, not found.
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Returns ObserverStateChange by comparing conditions for a connection.
 | 
						|
        /// </summary>
 | 
						|
        /// <returns>True if added to Observers.</returns>
 | 
						|
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
						|
        internal ObserverStateChange RebuildObservers(NetworkConnection connection, bool timedOnly)
 | 
						|
        {
 | 
						|
            bool currentlyAdded = (_networkObject.Observers.Contains(connection));
 | 
						|
            //True if all conditions are met.
 | 
						|
            bool allConditionsMet = true;
 | 
						|
 | 
						|
            //Only need to check beyond this if conditions exist.
 | 
						|
            if (_observerConditions.Count > 0)
 | 
						|
            {
 | 
						|
                /* If cnnection is owner then they can see the object. */
 | 
						|
                bool notOwner = (connection != _networkObject.Owner);
 | 
						|
 | 
						|
                /* Only check conditions if not owner. Owner will always
 | 
						|
                * have visibility. */
 | 
						|
                if (notOwner)
 | 
						|
                {
 | 
						|
                    bool parentVisible = true;
 | 
						|
                    if (_networkObject.ParentNetworkObject != null)
 | 
						|
                    {
 | 
						|
                        parentVisible = _networkObject.ParentNetworkObject.Observers.Contains(connection);
 | 
						|
                        /* If parent is visible but was not previously
 | 
						|
                         * then unset timedOnly to make sure all conditions
 | 
						|
                         * are checked again. This ensures that the _nonTimedMet
 | 
						|
                         * collection is updated. */
 | 
						|
                        if (parentVisible && !_lastParentVisible)
 | 
						|
                            timedOnly = false;
 | 
						|
                        _lastParentVisible = parentVisible;
 | 
						|
                    }
 | 
						|
 | 
						|
                    //If parent is not visible no further checks are required.
 | 
						|
                    if (!parentVisible)
 | 
						|
                    {
 | 
						|
                        allConditionsMet = false;
 | 
						|
                    }
 | 
						|
                    //Parent is visible, perform checks.
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        //True if connection starts with meeting non-timed conditions.
 | 
						|
                        bool startNonTimedMet = _nonTimedMet.Contains(connection);
 | 
						|
                        /* If a timed update an1d nonTimed
 | 
						|
                         * have not been met then there's
 | 
						|
                         * no reason to check timed. */
 | 
						|
                        if (timedOnly && !startNonTimedMet)
 | 
						|
                        {
 | 
						|
                            allConditionsMet = false;
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            //Becomes true if a non-timed condition fails.
 | 
						|
                            bool nonTimedMet = true;
 | 
						|
 | 
						|
                            List<ObserverCondition> collection = (timedOnly) ? _timedConditions : _observerConditions;
 | 
						|
                            for (int i = 0; i < collection.Count; i++)
 | 
						|
                            {
 | 
						|
                                ObserverCondition condition = collection[i];
 | 
						|
                                /* If any observer returns removed then break
 | 
						|
                                 * from loop and return removed. If one observer has
 | 
						|
                                 * removed then there's no reason to iterate
 | 
						|
                                 * the rest.
 | 
						|
                                 * 
 | 
						|
                                 * A condition is automatically met if it's not enabled. */
 | 
						|
                                bool notProcessed = false;
 | 
						|
                                bool conditionMet = (!condition.GetIsEnabled() || condition.ConditionMet(connection, currentlyAdded, out notProcessed));
 | 
						|
 | 
						|
                                if (notProcessed)
 | 
						|
                                    conditionMet = currentlyAdded;
 | 
						|
 | 
						|
                                //Condition not met.
 | 
						|
                                if (!conditionMet)
 | 
						|
                                {
 | 
						|
                                    allConditionsMet = false;
 | 
						|
                                    if (!condition.Timed())
 | 
						|
                                        nonTimedMet = false;
 | 
						|
                                    break;
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
 | 
						|
                            //If all conditions are being checked and nonTimedMet has updated.
 | 
						|
                            if (!timedOnly && (startNonTimedMet != nonTimedMet))
 | 
						|
                            {
 | 
						|
                                if (nonTimedMet)
 | 
						|
                                    _nonTimedMet.Add(connection);
 | 
						|
                                else
 | 
						|
                                    _nonTimedMet.Remove(connection);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //If all conditions met.
 | 
						|
            if (allConditionsMet)
 | 
						|
                return ReturnPassedConditions(currentlyAdded);
 | 
						|
            else
 | 
						|
                return ReturnFailedCondition(currentlyAdded);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Registers timed observer conditions.
 | 
						|
        /// </summary>
 | 
						|
        private void RegisterTimedConditions()
 | 
						|
        {
 | 
						|
            if (_timedConditions.Count == 0)
 | 
						|
                return;
 | 
						|
            //Already registered or no timed conditions.
 | 
						|
            if (_registeredAsTimed)
 | 
						|
                return;
 | 
						|
 | 
						|
            _registeredAsTimed = true;
 | 
						|
            _networkObject.NetworkManager.ServerManager.Objects.AddTimedNetworkObserver(_networkObject);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Unregisters timed conditions.
 | 
						|
        /// </summary>
 | 
						|
        private void UnregisterTimedConditions()
 | 
						|
        {
 | 
						|
            if (_timedConditions.Count == 0)
 | 
						|
                return;
 | 
						|
            if (!_registeredAsTimed)
 | 
						|
                return;
 | 
						|
 | 
						|
            _registeredAsTimed = false;
 | 
						|
            _networkObject.NetworkManager.ServerManager.Objects.RemoveTimedNetworkObserver(_networkObject);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Returns an ObserverStateChange when a condition fails.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="currentlyAdded"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        private ObserverStateChange ReturnFailedCondition(bool currentlyAdded)
 | 
						|
        {
 | 
						|
            if (currentlyAdded)
 | 
						|
                return ObserverStateChange.Removed;
 | 
						|
            else
 | 
						|
                return ObserverStateChange.Unchanged;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Returns an ObserverStateChange when all conditions pass.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="currentlyAdded"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        private ObserverStateChange ReturnPassedConditions(bool currentlyAdded)
 | 
						|
        {
 | 
						|
            if (currentlyAdded)
 | 
						|
                return ObserverStateChange.Unchanged;
 | 
						|
            else
 | 
						|
                return ObserverStateChange.Added;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Called when a remote client state changes with the server.
 | 
						|
        /// </summary>
 | 
						|
        private void ServerManager_OnRemoteConnectionState(NetworkConnection conn, RemoteConnectionStateArgs arg2)
 | 
						|
        {
 | 
						|
            if (arg2.ConnectionState == RemoteConnectionState.Stopped)
 | 
						|
                _nonTimedMet.Remove(conn);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Sets a new value for UpdateHostVisibility.
 | 
						|
        /// This does not immediately update renderers.
 | 
						|
        /// You may need to combine with NetworkObject.SetRenderersVisible(bool).
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="value">New value.</param>
 | 
						|
        public void SetUpdateHostVisibility(bool value)
 | 
						|
        {
 | 
						|
            //Unchanged.
 | 
						|
            if (value == UpdateHostVisibility)
 | 
						|
                return;
 | 
						|
 | 
						|
            UpdateHostVisibility = value;
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
}
 |