522 lines
21 KiB
C#
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);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
} |