StationObscurum/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs

522 lines
21 KiB
C#

using FishNet.Connection;
using FishNet.Managing.Object;
using FishNet.Managing.Transporting;
using FishNet.Object;
using FishNet.Observing;
using FishNet.Serializing;
using FishNet.Transporting;
using FishNet.Utility.Performance;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Managing.Server
{
public partial class ServerObjects : ManagedObjects
{
#region Private.
/// <summary>
/// Cache filled with objects which observers are being updated.
/// This is primarily used to invoke events after all observers are updated, rather than as each is updated.
/// </summary>
private List<NetworkObject> _observerChangedObjectsCache = new List<NetworkObject>(100);
/// <summary>
/// NetworkObservers which require regularly iteration.
/// </summary>
private List<NetworkObject> _timedNetworkObservers = new List<NetworkObject>();
/// <summary>
/// Index in TimedNetworkObservers to start on next cycle.
/// </summary>
private int _nextTimedObserversIndex;
#endregion
/// <summary>
/// Called when MonoBehaviours call Update.
/// </summary>
private void Observers_OnUpdate()
{
UpdateTimedObservers();
}
/// <summary>
/// Progressively updates NetworkObservers with timed conditions.
/// </summary>
private void UpdateTimedObservers()
{
if (!base.NetworkManager.IsServer)
return;
//No point in updating if the timemanager isn't going to tick this frame.
if (!base.NetworkManager.TimeManager.FrameTicked)
return;
int observersCount = _timedNetworkObservers.Count;
if (observersCount == 0)
return;
ServerManager serverManager = base.NetworkManager.ServerManager;
TransportManager transportManager = NetworkManager.TransportManager;
/* Try to iterate all timed observers every half a second.
* This value will increase as there's more observers. */
int completionTicks = (base.NetworkManager.TimeManager.TickRate * 2);
/* Multiply required ticks based on connection count and nob count. This will
* reduce how quickly observers update slightly but will drastically
* improve performance. */
float tickMultiplier = 1f + (float)(
(serverManager.Clients.Count * 0.005f) +
(_timedNetworkObservers.Count * 0.0005f)
);
/* Add an additional iteration to prevent
* 0 iterations */
int iterations = (observersCount / (int)(completionTicks * tickMultiplier)) + 1;
if (iterations > observersCount)
iterations = observersCount;
PooledWriter everyoneWriter = WriterPool.GetWriter();
PooledWriter ownerWriter = WriterPool.GetWriter();
//Index to perform a check on.
int observerIndex = 0;
foreach (NetworkConnection conn in serverManager.Clients.Values)
{
int cacheIndex = 0;
using (PooledWriter largeWriter = WriterPool.GetWriter())
{
//Reset index to start on for every connection.
observerIndex = 0;
/* Run the number of calculated iterations.
* This is spaced out over frames to prevent
* fps spikes. */
for (int i = 0; i < iterations; i++)
{
observerIndex = _nextTimedObserversIndex + i;
/* Compare actual collection size not cached value.
* This is incase collection is modified during runtime. */
if (observerIndex >= _timedNetworkObservers.Count)
observerIndex -= _timedNetworkObservers.Count;
/* If still out of bounds something whack is going on.
* Reset index and exit method. Let it sort itself out
* next iteration. */
if (observerIndex < 0 || observerIndex >= _timedNetworkObservers.Count)
{
_nextTimedObserversIndex = 0;
break;
}
NetworkObject nob = _timedNetworkObservers[observerIndex];
ObserverStateChange osc = nob.RebuildObservers(conn, true);
if (osc == ObserverStateChange.Added)
{
everyoneWriter.Reset();
ownerWriter.Reset();
base.WriteSpawn_Server(nob, conn, everyoneWriter, ownerWriter);
CacheObserverChange(nob, ref cacheIndex);
}
else if (osc == ObserverStateChange.Removed)
{
everyoneWriter.Reset();
WriteDespawn(nob, nob.GetDefaultDespawnType(), everyoneWriter);
}
else
{
continue;
}
/* Only use ownerWriter if an add, and if owner. Owner
* doesn't matter if not being added because no owner specific
* information would be included. */
PooledWriter writerToUse = (osc == ObserverStateChange.Added && nob.Owner == conn) ?
ownerWriter : everyoneWriter;
largeWriter.WriteArraySegment(writerToUse.GetArraySegment());
}
if (largeWriter.Length > 0)
{
transportManager.SendToClient(
(byte)Channel.Reliable,
largeWriter.GetArraySegment(), conn);
}
//Invoke spawn callbacks on nobs.
for (int i = 0; i < cacheIndex; i++)
_observerChangedObjectsCache[i].InvokePostOnServerStart(conn);
}
}
everyoneWriter.Dispose();
ownerWriter.Dispose();
_nextTimedObserversIndex = (observerIndex + 1);
}
/// <summary>
/// Indicates that a networkObserver component should be updated regularly. This is done automatically.
/// </summary>
/// <param name="networkObject">NetworkObject to be updated.</param>
public void AddTimedNetworkObserver(NetworkObject networkObject)
{
_timedNetworkObservers.Add(networkObject);
}
/// <summary>
/// Indicates that a networkObserver component no longer needs to be updated regularly. This is done automatically.
/// </summary>
/// <param name="networkObject">NetworkObject to be updated.</param>
public void RemoveTimedNetworkObserver(NetworkObject networkObject)
{
_timedNetworkObservers.Remove(networkObject);
}
/// <summary>
/// Caches an observer change.
/// </summary>
/// <param name="cacheIndex"></param>
private void CacheObserverChange(NetworkObject nob, ref int cacheIndex)
{
/* If this spawn would exceed cache size then
* add instead of set value. */
if (_observerChangedObjectsCache.Count <= cacheIndex)
_observerChangedObjectsCache.Add(nob);
else
_observerChangedObjectsCache[cacheIndex] = nob;
cacheIndex++;
}
/// <summary>
/// Removes a connection from observers without synchronizing changes.
/// </summary>
/// <param name="connection"></param>
private void RemoveFromObserversWithoutSynchronization(NetworkConnection connection)
{
int cacheIndex = 0;
foreach (NetworkObject nob in Spawned.Values)
{
if (nob.RemoveObserver(connection))
CacheObserverChange(nob, ref cacheIndex);
}
//Invoke despawn callbacks on nobs.
for (int i = 0; i < cacheIndex; i++)
_observerChangedObjectsCache[i].InvokeOnServerDespawn(connection);
}
/// <summary>
/// Rebuilds observers on all NetworkObjects for all connections.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers()
{
ListCache<NetworkObject> nobCache = GetOrderedSpawnedObjects();
ListCache<NetworkConnection> connCache = ListCaches.GetNetworkConnectionCache();
foreach (NetworkConnection conn in base.NetworkManager.ServerManager.Clients.Values)
connCache.AddValue(conn);
RebuildObservers(nobCache, connCache);
ListCaches.StoreCache(nobCache);
ListCaches.StoreCache(connCache);
}
/// <summary>
/// Rebuilds observers on NetworkObjects.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(NetworkObject[] nobs)
{
int count = nobs.Length;
for (int i = 0; i < count; i++)
RebuildObservers(nobs[i]);
}
/// <summary>
/// Rebuilds observers on NetworkObjects.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(List<NetworkObject> nobs)
{
int count = nobs.Count;
for (int i = 0; i < count; i++)
RebuildObservers(nobs[i]);
}
/// <summary>
/// Rebuilds observers on NetworkObjects.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(ListCache<NetworkObject> nobs)
{
int count = nobs.Written;
List<NetworkObject> collection = nobs.Collection;
for (int i = 0; i < count; i++)
RebuildObservers(collection[i]);
}
/// <summary>
/// Rebuilds observers on NetworkObjects for connections.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(ListCache<NetworkObject> nobs, NetworkConnection conn)
{
RebuildObservers(nobs.Collection, conn, nobs.Written);
}
/// <summary>
/// Rebuilds observers on NetworkObjects for connections.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(ListCache<NetworkObject> nobs, ListCache<NetworkConnection> conns)
{
int count = nobs.Written;
List<NetworkObject> collection = nobs.Collection;
for (int i = 0; i < count; i++)
RebuildObservers(collection[i], conns);
}
/// <summary>
/// Rebuilds observers on all objects for a connections.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(ListCache<NetworkConnection> connections)
{
int count = connections.Written;
List<NetworkConnection> collection = connections.Collection;
for (int i = 0; i < count; i++)
RebuildObservers(collection[i]);
}
/// <summary>
/// Rebuilds observers on all objects for connections.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(NetworkConnection[] connections)
{
int count = connections.Length;
for (int i = 0; i < count; i++)
RebuildObservers(connections[i]);
}
/// <summary>
/// Rebuilds observers on all objects for connections.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(List<NetworkConnection> connections)
{
int count = connections.Count;
for (int i = 0; i < count; i++)
RebuildObservers(connections[i]);
}
/// <summary>
/// Rebuilds observers on all NetworkObjects for a connection.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(NetworkConnection connection)
{
ListCache<NetworkObject> cache = GetOrderedSpawnedObjects();
RebuildObservers(cache, connection);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Gets all spawned objects with root objects first.
/// </summary>
/// <returns></returns>
private ListCache<NetworkObject> GetOrderedSpawnedObjects()
{
ListCache<NetworkObject> cache = ListCaches.GetNetworkObjectCache();
foreach (NetworkObject networkObject in Spawned.Values)
{
if (networkObject.IsNested)
continue;
//Add nob and children recursively.
AddChildNetworkObjects(networkObject);
}
void AddChildNetworkObjects(NetworkObject n)
{
cache.AddValue(n);
foreach (NetworkObject nob in n.ChildNetworkObjects)
AddChildNetworkObjects(nob);
}
return cache;
}
/// <summary>
/// Rebuilds observers for a connection on NetworkObjects.
/// </summary>
/// <param name="nobs">NetworkObjects to rebuild.</param>
/// <param name="connection">Connection to rebuild for.</param>
/// <param name="count">Number of iterations to perform collection. Entire collection is iterated when value is -1.</param>
public void RebuildObservers(IEnumerable<NetworkObject> nobs, NetworkConnection connection, int count = -1)
{
PooledWriter everyoneWriter = WriterPool.GetWriter();
PooledWriter ownerWriter = WriterPool.GetWriter();
//If there's no limit on how many can be written set count to the maximum.
if (count == -1)
count = int.MaxValue;
int iterations;
int observerCacheIndex;
using (PooledWriter largeWriter = WriterPool.GetWriter())
{
iterations = 0;
observerCacheIndex = 0;
foreach (NetworkObject n in nobs)
{
iterations++;
if (iterations > count)
break;
//If observer state changed then write changes.
ObserverStateChange osc = n.RebuildObservers(connection, false);
if (osc == ObserverStateChange.Added)
{
everyoneWriter.Reset();
ownerWriter.Reset();
base.WriteSpawn_Server(n, connection, everyoneWriter, ownerWriter);
CacheObserverChange(n, ref observerCacheIndex);
}
else if (osc == ObserverStateChange.Removed)
{
connection.LevelOfDetails.Remove(n);
everyoneWriter.Reset();
WriteDespawn(n, n.GetDefaultDespawnType(), everyoneWriter);
}
else
{
continue;
}
/* Only use ownerWriter if an add, and if owner. Owner //cleanup see if rebuild timed and this can be joined or reuse methods.
* doesn't matter if not being added because no owner specific
* information would be included. */
PooledWriter writerToUse = (osc == ObserverStateChange.Added && n.Owner == connection) ?
ownerWriter : everyoneWriter;
largeWriter.WriteArraySegment(writerToUse.GetArraySegment());
}
if (largeWriter.Length > 0)
{
NetworkManager.TransportManager.SendToClient(
(byte)Channel.Reliable,
largeWriter.GetArraySegment(), connection);
}
}
//Dispose of writers created in this method.
everyoneWriter.Dispose();
ownerWriter.Dispose();
//Invoke spawn callbacks on nobs.
for (int i = 0; i < observerCacheIndex; i++)
_observerChangedObjectsCache[i].InvokePostOnServerStart(connection);
}
/// <summary>
/// Rebuilds observers for connections on a NetworkObject.
/// </summary>
private void RebuildObservers(NetworkObject nob, ListCache<NetworkConnection> conns)
{
PooledWriter everyoneWriter = WriterPool.GetWriter();
PooledWriter ownerWriter = WriterPool.GetWriter();
int written = conns.Written;
for (int i = 0; i < written; i++)
{
NetworkConnection conn = conns.Collection[i];
everyoneWriter.Reset();
ownerWriter.Reset();
//If observer state changed then write changes.
ObserverStateChange osc = nob.RebuildObservers(conn, false);
if (osc == ObserverStateChange.Added)
{
base.WriteSpawn_Server(nob, conn, everyoneWriter, ownerWriter);
}
else if (osc == ObserverStateChange.Removed)
{
conn.LevelOfDetails.Remove(nob);
WriteDespawn(nob, nob.GetDefaultDespawnType(), everyoneWriter);
}
else
{
continue;
}
/* Only use ownerWriter if an add, and if owner. Owner
* doesn't matter if not being added because no owner specific
* information would be included. */
PooledWriter writerToUse = (osc == ObserverStateChange.Added && nob.Owner == conn) ?
ownerWriter : everyoneWriter;
if (writerToUse.Length > 0)
{
NetworkManager.TransportManager.SendToClient(
(byte)Channel.Reliable,
writerToUse.GetArraySegment(), conn);
//If a spawn is being sent.
if (osc == ObserverStateChange.Added)
nob.InvokePostOnServerStart(conn);
}
}
//Dispose of writers created in this method.
everyoneWriter.Dispose();
ownerWriter.Dispose();
}
/// <summary>
/// Rebuilds observers for all connections for a NetworkObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(NetworkObject nob)
{
ListCache<NetworkConnection> cache = ListCaches.GetNetworkConnectionCache();
foreach (NetworkConnection item in NetworkManager.ServerManager.Clients.Values)
cache.AddValue(item);
RebuildObservers(nob, cache);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Rebuilds observers for a connection on NetworkObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RebuildObservers(NetworkObject nob, NetworkConnection conn)
{
ListCache<NetworkConnection> cache = ListCaches.GetNetworkConnectionCache();
cache.AddValue(conn);
RebuildObservers(nob, cache);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Rebuilds observers for connections on NetworkObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(NetworkObject networkObject, NetworkConnection[] connections)
{
ListCache<NetworkConnection> cache = ListCaches.GetNetworkConnectionCache();
cache.AddValues(connections);
RebuildObservers(networkObject, cache);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Rebuilds observers for connections on NetworkObject.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RebuildObservers(NetworkObject networkObject, List<NetworkConnection> connections)
{
ListCache<NetworkConnection> cache = ListCaches.GetNetworkConnectionCache();
cache.AddValues(connections);
RebuildObservers(networkObject, cache);
ListCaches.StoreCache(cache);
}
}
}