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);
}
}
}