forked from cgvr/DeltaVR
deltavr multiplayer 2.0
This commit is contained in:
@@ -0,0 +1,522 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user