Unity-WebSocket/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs
2025-06-28 11:28:54 +03:30

225 lines
8.9 KiB
C#

using FishNet.Component.Observing;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Observing;
using FishNet.Utility;
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
{
/// <summary>
/// Additional options for managing the observer system.
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("FishNet/Manager/ObserverManager")]
public sealed class ObserverManager : MonoBehaviour
{
#region Serialized.
/// <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;
}
[Tooltip("True to update visibility for clientHost based on if they are an observer or not.")]
[SerializeField]
private bool _updateHostVisibility = true;
/// <summary>
/// Maximum duration the server will take to update timed observer conditions as server load increases. Lower values will result in timed conditions being checked quicker at the cost of performance..
/// </summary>
public float MaximumTimedObserversDuration
{
get => _maximumTimedObserversDuration;
private set => _maximumTimedObserversDuration = value;
}
[Tooltip("Maximum duration the server will take to update timed observer conditions as server load increases. Lower values will result in timed conditions being checked quicker at the cost of performance.")]
[SerializeField]
[Range(MINIMUM_TIMED_OBSERVERS_DURATION, MAXIMUM_TIMED_OBSERVERS_DURATION)]
private float _maximumTimedObserversDuration = 10f;
/// <summary>
/// Sets the MaximumTimedObserversDuration value.
/// </summary>
/// <param name="value">New maximum duration to update timed observers over.</param>
public void SetMaximumTimedObserversDuration(float value) => MaximumTimedObserversDuration = System.Math.Clamp(value, MINIMUM_TIMED_OBSERVERS_DURATION, MAXIMUM_TIMED_OBSERVERS_DURATION);
/// <summary>
///
/// </summary>
[Tooltip("Default observer conditions for networked objects.")]
[SerializeField]
private List<ObserverCondition> _defaultConditions = new();
#endregion
#region Private.
/// <summary>
/// NetworkManager on object.
/// </summary>
private NetworkManager _networkManager;
#endregion
#region Consts.
/// <summary>
/// Minimum time allowed for timed observers to rebuild.
/// </summary>
private const float MINIMUM_TIMED_OBSERVERS_DURATION = 0.1f;
/// <summary>
/// Maxmimum time allowed for timed observers to rebuild.
/// </summary>
private const float MAXIMUM_TIMED_OBSERVERS_DURATION = 20f;
#endregion
/// <summary>
/// Initializes this script for use.
/// </summary>
/// <param name="manager"></param>
internal void InitializeOnce_Internal(NetworkManager manager)
{
_networkManager = manager;
//Update the current value to itself so it becomes clamped. This is just to protect against the user manually setting it outside clamp somehow.
SetMaximumTimedObserversDuration(MaximumTimedObserversDuration);
}
/// <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.IsServerStarted && 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(out result))
{
obsAdded = true;
result = nob.gameObject.AddComponent<NetworkObserver>();
}
else
{
//If already setup by this manager then return.
if (result.ConditionsSetByObserverManager)
return result;
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);
}
}
result.ConditionsSetByObserverManager = true;
return result;
}
}
}