using FishNet.Connection; using FishNet.Observing; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; namespace FishNet.Object { public sealed partial class NetworkObject : MonoBehaviour { #region Public. /// /// Called when this NetworkObject losses all observers or gains observers while previously having none. /// public event Action OnObserversActive; /// /// NetworkObserver on this object. /// [HideInInspector] public NetworkObserver NetworkObserver = null; /// /// Clients which can see and get messages from this NetworkObject. /// public HashSet Observers = new HashSet(); #endregion #region Private. /// /// True if NetworkObserver has been initialized. /// private bool _networkObserverInitiliazed = false; /// /// Found renderers on the NetworkObject and it's children. This is only used as clientHost to hide non-observers objects. /// [System.NonSerialized] private Renderer[] _renderers; /// /// True if renderers have been looked up. /// private bool _renderersPopulated; /// /// Last visibility value for clientHost on this object. /// private bool _lastClientHostVisibility; #endregion /// /// Updates cached renderers used to managing clientHost visibility. /// /// True to also update visibility if clientHost. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UpdateRenderers(bool updateVisibility = true) { UpdateRenderers_Internal(updateVisibility); } /// /// Sets the renderer visibility for clientHost. /// /// True if renderers are to be visibile. /// True to skip blocking checks. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetRenderersVisible(bool visible, bool force = false) { if (!force) { if (!NetworkObserver.UpdateHostVisibility) return; } if (!_renderersPopulated) { UpdateRenderers_Internal(false); _renderersPopulated = true; } UpdateRenderVisibility(visible); } /// /// Clears and updates renderers. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateRenderers_Internal(bool updateVisibility) { _renderers = GetComponentsInChildren(true); List enabledRenderers = new List(); foreach (Renderer r in _renderers) { if (r.enabled) enabledRenderers.Add(r); } //If there are any disabled renderers then change _renderers to cached values. if (enabledRenderers.Count != _renderers.Length) _renderers = enabledRenderers.ToArray(); if (updateVisibility) UpdateRenderVisibility(_lastClientHostVisibility); } /// /// Updates visibilites on renders without checks. /// /// private void UpdateRenderVisibility(bool visible) { bool rebuildRenderers = false; Renderer[] rs = _renderers; int count = rs.Length; for (int i = 0; i < count; i++) { Renderer r = rs[i]; if (r == null) { rebuildRenderers = true; break; } r.enabled = visible; } _lastClientHostVisibility = visible; //If to rebuild then do so, while updating visibility. if (rebuildRenderers) UpdateRenderers(true); } /// /// Adds the default NetworkObserver conditions using the ObserverManager. /// private void AddDefaultNetworkObserverConditions() { if (_networkObserverInitiliazed) return; NetworkObserver = NetworkManager.ObserverManager.AddDefaultConditions(this); } /// /// Removes a connection from observers for this object returning if the connection was removed. /// /// internal bool RemoveObserver(NetworkConnection connection) { int startCount = Observers.Count; bool removed = Observers.Remove(connection); if (removed) TryInvokeOnObserversActive(startCount); return removed; } /// /// Adds the connection to observers if conditions are met. /// /// /// True if added to Observers. internal ObserverStateChange RebuildObservers(NetworkConnection connection, bool timedOnly) { //If not a valid connection. if (!connection.IsValid) { NetworkManager.LogWarning($"An invalid connection was used when rebuilding observers."); return ObserverStateChange.Unchanged; } //Valid not not active. else if (!connection.IsActive) { /* Just remove from observers since connection isn't active * and return unchanged because nothing should process * given the connection isnt active. */ Observers.Remove(connection); return ObserverStateChange.Unchanged; } else if (IsDeinitializing) { /* If object is deinitializing it's either being despawned * this frame or it's not spawned. If we've made it this far, * it's most likely being despawned. */ return ObserverStateChange.Unchanged; } int startCount = Observers.Count; ObserverStateChange osc = NetworkObserver.RebuildObservers(connection, timedOnly); if (osc == ObserverStateChange.Added) Observers.Add(connection); else if (osc == ObserverStateChange.Removed) Observers.Remove(connection); if (osc != ObserverStateChange.Unchanged) TryInvokeOnObserversActive(startCount); return osc; } /// /// Invokes OnObserversActive if observers are now 0 but previously were not, or if was previously 0 but now has observers. /// /// private void TryInvokeOnObserversActive(int startCount) { if ((Observers.Count > 0 && startCount == 0) || Observers.Count == 0 && startCount > 0) OnObserversActive?.Invoke(this); } } }