mirror of
https://github.com/Dadechin/Unity-WebSocket.git
synced 2025-07-03 20:04:33 +00:00
460 lines
18 KiB
C#
460 lines
18 KiB
C#
using FishNet.Connection;
|
|
using FishNet.Managing.Object;
|
|
using FishNet.Managing.Timing;
|
|
using FishNet.Object;
|
|
using FishNet.Observing;
|
|
using FishNet.Serializing;
|
|
using FishNet.Transporting;
|
|
using GameKit.Dependencies.Utilities;
|
|
using System.Collections.Generic;
|
|
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(100);
|
|
/// <summary>
|
|
/// NetworkObservers which require regularly iteration.
|
|
/// </summary>
|
|
private List<NetworkObject> _timedNetworkObservers = new();
|
|
/// <summary>
|
|
/// Index in TimedNetworkObservers to start on next cycle.
|
|
/// </summary>
|
|
private int _nextTimedObserversIndex;
|
|
/// <summary>
|
|
/// Used to write spawns for everyone. This writer will exclude owner only information.
|
|
/// </summary>
|
|
private PooledWriter _writer = new();
|
|
/// <summary>
|
|
/// Indexes within TimedNetworkObservers which are unset.
|
|
/// </summary>
|
|
private Queue<int> _emptiedTimedIndexes = new();
|
|
#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.IsServerStarted)
|
|
return;
|
|
//No point in updating if the timemanager isn't going to tick this frame.
|
|
if (!base.NetworkManager.TimeManager.FrameTicked)
|
|
return;
|
|
int networkObserversCount = _timedNetworkObservers.Count;
|
|
if (networkObserversCount == 0)
|
|
return;
|
|
|
|
/* Try to iterate all timed observers every half a second.
|
|
* This value will increase as there's more observers or timed conditions. */
|
|
float timeMultiplier = 1f + ((base.NetworkManager.ServerManager.Clients.Count * 0.005f) + (_timedNetworkObservers.Count * 0.0005f));
|
|
//Check cap this way for readability.
|
|
float completionTime = Mathf.Min((0.5f * timeMultiplier), base.NetworkManager.ObserverManager.MaximumTimedObserversDuration);
|
|
uint completionTicks = base.NetworkManager.TimeManager.TimeToTicks(completionTime, TickRounding.RoundUp);
|
|
/* Iterations will be the number of objects
|
|
* to iterate to be have completed all objects by
|
|
* the end of completionTicks. */
|
|
int iterations = Mathf.CeilToInt((float)networkObserversCount / completionTicks);
|
|
if (iterations > _timedNetworkObservers.Count)
|
|
iterations = _timedNetworkObservers.Count;
|
|
|
|
List<NetworkConnection> connCache = RetrieveAuthenticatedConnections();
|
|
//Build nob cache.
|
|
List<NetworkObject> nobCache = CollectionCaches<NetworkObject>.RetrieveList();
|
|
for (int i = 0; i < iterations; i++)
|
|
{
|
|
if (_nextTimedObserversIndex >= _timedNetworkObservers.Count)
|
|
_nextTimedObserversIndex = 0;
|
|
|
|
NetworkObject nob = _timedNetworkObservers[_nextTimedObserversIndex++];
|
|
if (nob != null)
|
|
nobCache.Add(nob);
|
|
}
|
|
|
|
RebuildObservers(nobCache, connCache, true);
|
|
|
|
CollectionCaches<NetworkConnection>.Store(connCache);
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
if (_emptiedTimedIndexes.TryDequeue(out int index))
|
|
_timedNetworkObservers[index] = networkObject;
|
|
else
|
|
_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)
|
|
{
|
|
int index = _timedNetworkObservers.IndexOf(networkObject);
|
|
if (index == -1)
|
|
return;
|
|
|
|
_emptiedTimedIndexes.Enqueue(index);
|
|
_timedNetworkObservers[index] = null;
|
|
|
|
//If there's a decent amount missing then rebuild the collection.
|
|
if (_emptiedTimedIndexes.Count > 20)
|
|
{
|
|
List<NetworkObject> newLst = CollectionCaches<NetworkObject>.RetrieveList();
|
|
foreach (NetworkObject nob in _timedNetworkObservers)
|
|
{
|
|
if (nob == null)
|
|
continue;
|
|
|
|
newLst.Add(nob);
|
|
}
|
|
|
|
CollectionCaches<NetworkObject>.Store(_timedNetworkObservers);
|
|
_timedNetworkObservers = newLst;
|
|
_emptiedTimedIndexes.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all NetworkConnections which are authenticated.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<NetworkConnection> RetrieveAuthenticatedConnections()
|
|
{
|
|
List<NetworkConnection> cache = CollectionCaches<NetworkConnection>.RetrieveList();
|
|
foreach (NetworkConnection item in NetworkManager.ServerManager.Clients.Values)
|
|
{
|
|
if (item.IsAuthenticated)
|
|
cache.Add(item);
|
|
}
|
|
|
|
return cache;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all spawned objects with root objects first.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<NetworkObject> RetrieveOrderedSpawnedObjects()
|
|
{
|
|
List<NetworkObject> spawnedCache = GetSpawnedNetworkObjects();
|
|
|
|
List<NetworkObject> sortedCache = SortRootAndNestedByInitializeOrder(spawnedCache);
|
|
|
|
CollectionCaches<NetworkObject>.Store(spawnedCache);
|
|
|
|
return sortedCache;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns spawned NetworkObjects as a list.
|
|
/// Collection returned is a new cache and should be disposed of properly.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<NetworkObject> GetSpawnedNetworkObjects()
|
|
{
|
|
List<NetworkObject> cache = CollectionCaches<NetworkObject>.RetrieveList();
|
|
Spawned.ValuesToList(ref cache);
|
|
|
|
return cache;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts a collection of NetworkObjects root and nested by initialize order.
|
|
/// Collection returned is a new cache and should be disposed of properly.
|
|
/// </summary>
|
|
internal List<NetworkObject> SortRootAndNestedByInitializeOrder(List<NetworkObject> nobs)
|
|
{
|
|
List<NetworkObject> sortedRootCache = CollectionCaches<NetworkObject>.RetrieveList();
|
|
|
|
//First order root objects.
|
|
foreach (NetworkObject item in nobs)
|
|
{
|
|
if (item.IsNested)
|
|
continue;
|
|
|
|
sortedRootCache.AddOrdered(item);
|
|
}
|
|
|
|
/* After all root are ordered check
|
|
* their nested. Order nested in segments
|
|
* of each root then insert after the root.
|
|
* This must be performed after all roots are ordered. */
|
|
|
|
//This holds the results of all values.
|
|
List<NetworkObject> sortedRootAndNestedCache = CollectionCaches<NetworkObject>.RetrieveList();
|
|
|
|
//Cache for sorting nested.
|
|
List<NetworkObject> sortedNestedCache = CollectionCaches<NetworkObject>.RetrieveList();
|
|
|
|
foreach (NetworkObject item in sortedRootCache)
|
|
{
|
|
/* Remove recursive and only check Initialized and Runtime. Once iterated
|
|
* check each added entry again using Initialized and Recursive. */
|
|
List<NetworkObject> nested = item.GetNetworkObjects(GetNetworkObjectOption.AllNestedRecursive);
|
|
foreach (NetworkObject nestedItem in nested)
|
|
{
|
|
if (sortedNestedCache.Contains(nestedItem))
|
|
Debug.LogError("Already contains " + nestedItem.name);
|
|
else
|
|
sortedNestedCache.AddOrdered(nestedItem);
|
|
}
|
|
|
|
CollectionCaches<NetworkObject>.Store(nested);
|
|
|
|
/* Once all nested are sorted then can be added to the
|
|
* sorted root and nested cache. */
|
|
sortedRootAndNestedCache.Add(item);
|
|
sortedRootAndNestedCache.AddRange(sortedNestedCache);
|
|
|
|
//Reset cache.
|
|
sortedNestedCache.Clear();
|
|
}
|
|
|
|
//Store temp caches.
|
|
CollectionCaches<NetworkObject>.Store(sortedRootCache);
|
|
CollectionCaches<NetworkObject>.Store(sortedNestedCache);
|
|
|
|
return sortedRootAndNestedCache;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a connection from observers without synchronizing changes.
|
|
/// </summary>
|
|
/// <param name="connection"></param>
|
|
private void RemoveFromObserversWithoutSynchronization(NetworkConnection connection)
|
|
{
|
|
List<NetworkObject> observerChangedObjectsCache = _observerChangedObjectsCache;
|
|
foreach (NetworkObject nob in Spawned.Values)
|
|
{
|
|
if (nob.RemoveObserver(connection))
|
|
observerChangedObjectsCache.Add(nob);
|
|
}
|
|
|
|
//Invoke despawn callbacks on nobs.
|
|
for (int i = 0; i < observerChangedObjectsCache.Count; i++)
|
|
observerChangedObjectsCache[i].InvokeOnServerDespawn(connection);
|
|
observerChangedObjectsCache.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers on all NetworkObjects for all connections.
|
|
/// </summary>
|
|
public void RebuildObservers(bool timedOnly = false)
|
|
{
|
|
List<NetworkObject> nobCache = RetrieveOrderedSpawnedObjects();
|
|
List<NetworkConnection> connCache = RetrieveAuthenticatedConnections();
|
|
|
|
RebuildObservers(nobCache, connCache, timedOnly);
|
|
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
CollectionCaches<NetworkConnection>.Store(connCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers for all connections for a NetworkObject.
|
|
/// </summary>
|
|
public void RebuildObservers(NetworkObject nob, bool timedOnly = false)
|
|
{
|
|
List<NetworkObject> nobCache = CollectionCaches<NetworkObject>.RetrieveList(nob);
|
|
List<NetworkConnection> connCache = RetrieveAuthenticatedConnections();
|
|
|
|
RebuildObservers(nobCache, connCache, timedOnly);
|
|
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
CollectionCaches<NetworkConnection>.Store(connCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers on all NetworkObjects for a connection.
|
|
/// </summary>
|
|
public void RebuildObservers(NetworkConnection connection, bool timedOnly = false)
|
|
{
|
|
List<NetworkObject> nobCache = RetrieveOrderedSpawnedObjects();
|
|
List<NetworkConnection> connCache = CollectionCaches<NetworkConnection>.RetrieveList(connection);
|
|
|
|
RebuildObservers(nobCache, connCache, timedOnly);
|
|
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
CollectionCaches<NetworkConnection>.Store(connCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers on NetworkObjects.
|
|
/// </summary>
|
|
public void RebuildObservers(IList<NetworkObject> nobs, bool timedOnly = false)
|
|
{
|
|
List<NetworkConnection> conns = RetrieveAuthenticatedConnections();
|
|
|
|
RebuildObservers(nobs, conns, timedOnly);
|
|
|
|
CollectionCaches<NetworkConnection>.Store(conns);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers on all objects for connections.
|
|
/// </summary>
|
|
public void RebuildObservers(IList<NetworkConnection> connections, bool timedOnly = false)
|
|
{
|
|
List<NetworkObject> nobCache = RetrieveOrderedSpawnedObjects();
|
|
|
|
RebuildObservers(nobCache, connections, timedOnly);
|
|
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers on NetworkObjects for connections.
|
|
/// </summary>
|
|
public void RebuildObservers(IList<NetworkObject> nobs, NetworkConnection conn, bool timedOnly = false)
|
|
{
|
|
List<NetworkConnection> connCache = CollectionCaches<NetworkConnection>.RetrieveList(conn);
|
|
|
|
RebuildObservers(nobs, connCache, timedOnly);
|
|
|
|
CollectionCaches<NetworkConnection>.Store(connCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers for connections on NetworkObject.
|
|
/// </summary>
|
|
public void RebuildObservers(NetworkObject networkObject, IList<NetworkConnection> connections, bool timedOnly = false)
|
|
{
|
|
List<NetworkObject> nobCache = CollectionCaches<NetworkObject>.RetrieveList(networkObject);
|
|
|
|
RebuildObservers(nobCache, connections, timedOnly);
|
|
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers on NetworkObjects for connections.
|
|
/// </summary>
|
|
public void RebuildObservers(IList<NetworkObject> nobs, IList<NetworkConnection> conns, bool timedOnly = false)
|
|
{
|
|
List<NetworkObject> nobCache = CollectionCaches<NetworkObject>.RetrieveList();
|
|
NetworkConnection nc;
|
|
|
|
int connsCount = conns.Count;
|
|
for (int i = 0; i < connsCount; i++)
|
|
{
|
|
nobCache.Clear();
|
|
|
|
nc = conns[i];
|
|
int nobsCount = nobs.Count;
|
|
for (int z = 0; z < nobsCount; z++)
|
|
RebuildObservers(nobs[z], nc, nobCache, timedOnly);
|
|
|
|
//Send if change.
|
|
if (_writer.Length > 0)
|
|
{
|
|
NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, _writer.GetArraySegment(), nc);
|
|
_writer.Clear();
|
|
|
|
foreach (NetworkObject n in nobCache)
|
|
n.OnSpawnServer(nc);
|
|
}
|
|
}
|
|
|
|
CollectionCaches<NetworkObject>.Store(nobCache);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers for a connection on NetworkObject.
|
|
/// </summary>
|
|
public void RebuildObservers(NetworkObject nob, NetworkConnection conn, bool timedOnly = false)
|
|
{
|
|
if (ApplicationState.IsQuitting())
|
|
return;
|
|
_writer.Clear();
|
|
|
|
conn.UpdateHashGridPositions(!timedOnly);
|
|
//If observer state changed then write changes.
|
|
ObserverStateChange osc = nob.RebuildObservers(conn, timedOnly);
|
|
if (osc == ObserverStateChange.Added)
|
|
{
|
|
WriteSpawn(nob, _writer, conn);
|
|
}
|
|
else if (osc == ObserverStateChange.Removed)
|
|
{
|
|
nob.InvokeOnServerDespawn(conn);
|
|
WriteDespawn(nob, nob.GetDefaultDespawnType(), _writer);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, _writer.GetArraySegment(), conn);
|
|
|
|
/* If spawning then also invoke server
|
|
* start events, such as buffer last
|
|
* and onspawnserver. */
|
|
if (osc == ObserverStateChange.Added)
|
|
nob.OnSpawnServer(conn);
|
|
|
|
_writer.Clear();
|
|
|
|
/* If there is change then also rebuild recursive networkObjects. */
|
|
foreach (NetworkBehaviour item in nob.RuntimeChildNetworkBehaviours)
|
|
RebuildObservers(item.NetworkObject, conn, timedOnly);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuilds observers for a connection on NetworkObject.
|
|
/// </summary>
|
|
internal void RebuildObservers(NetworkObject nob, NetworkConnection conn, List<NetworkObject> addedNobs, bool timedOnly = false)
|
|
{
|
|
if (ApplicationState.IsQuitting())
|
|
return;
|
|
|
|
/* When not using a timed rebuild such as this connections must have
|
|
* hashgrid data rebuilt immediately. */
|
|
conn.UpdateHashGridPositions(!timedOnly);
|
|
|
|
//If observer state changed then write changes.
|
|
ObserverStateChange osc = nob.RebuildObservers(conn, timedOnly);
|
|
if (osc == ObserverStateChange.Added)
|
|
{
|
|
WriteSpawn(nob, _writer, conn);
|
|
addedNobs.Add(nob);
|
|
}
|
|
else if (osc == ObserverStateChange.Removed)
|
|
{
|
|
nob.InvokeOnServerDespawn(conn);
|
|
WriteDespawn(nob, nob.GetDefaultDespawnType(), _writer);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* If there is change then also rebuild on any runtime children.
|
|
* This is to ensure runtime children have visibility updated
|
|
* in relation to parent.
|
|
*
|
|
* If here there is change. */
|
|
foreach (NetworkBehaviour item in nob.RuntimeChildNetworkBehaviours)
|
|
RebuildObservers(item.NetworkObject, conn, addedNobs, timedOnly);
|
|
}
|
|
}
|
|
} |