forked from cgvr/DeltaVR
deltavr multiplayer 2.0
This commit is contained in:
197
Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs
Normal file
197
Assets/FishNet/Runtime/Managing/Client/ClientManager.LOD.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using FishNet.Managing.Timing;
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Transporting;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Managing.Client
|
||||
{
|
||||
public sealed partial class ClientManager : MonoBehaviour
|
||||
{
|
||||
#region Internal.
|
||||
/// <summary>
|
||||
/// How many ticks between each LOD update.
|
||||
/// </summary>
|
||||
public uint LevelOfDetailInterval => NetworkManager.TimeManager.TimeToTicks(0.5d, TickRounding.RoundUp);
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Positions of the player objects.
|
||||
/// </summary>
|
||||
private List<Vector3> _objectsPositionsCache = new List<Vector3>();
|
||||
/// <summary>
|
||||
/// Next index within Spawned to update the LOD on.
|
||||
/// </summary>
|
||||
private int _nextLodNobIndex;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sends a level of update if conditions are met.
|
||||
/// </summary>
|
||||
/// <param name="forceFullUpdate">True to force send a full update immediately.
|
||||
/// This may be useful when teleporting clients.
|
||||
/// This must be called by on the server by using ServerManager.ForceLodUpdate(NetworkConnection).
|
||||
/// </param>
|
||||
internal void SendLodUpdate(bool forceFullUpdate)
|
||||
{
|
||||
if (!Connection.Authenticated)
|
||||
return;
|
||||
NetworkManager nm = NetworkManager;
|
||||
if (forceFullUpdate)
|
||||
{
|
||||
nm.LogError($"ForceFullUpdate is not yet implemented. Setting this true should not be possible.");
|
||||
return;
|
||||
}
|
||||
if (!nm.ObserverManager.GetUseNetworkLod())
|
||||
return;
|
||||
|
||||
//Interval check.
|
||||
uint localTick = nm.TimeManager.LocalTick;
|
||||
uint intervalRequirement = LevelOfDetailInterval;
|
||||
bool intervalMet = ((localTick - Connection.LastLevelOfDetailUpdate) >= intervalRequirement);
|
||||
if (!forceFullUpdate && !intervalMet)
|
||||
return;
|
||||
|
||||
//Set next tick.
|
||||
Connection.LastLevelOfDetailUpdate = localTick;
|
||||
|
||||
List<NetworkObject> localClientSpawned = nm.ClientManager.Objects.LocalClientSpawned;
|
||||
int spawnedCount = localClientSpawned.Count;
|
||||
if (spawnedCount == 0)
|
||||
return;
|
||||
|
||||
//Rebuild position cache for players objects.
|
||||
_objectsPositionsCache.Clear();
|
||||
foreach (NetworkObject playerObjects in Connection.Objects)
|
||||
_objectsPositionsCache.Add(playerObjects.transform.position);
|
||||
|
||||
/* Set the maximum number of entries per send.
|
||||
* Each send is going to be approximately 3 bytes
|
||||
* but sometimes can be 4. Calculate based off the maximum
|
||||
* possible bytes. */
|
||||
//int mtu = NetworkManager.TransportManager.GetMTU((byte)Channel.Reliable);
|
||||
const int estimatedMaximumIterations = ( 400 / 4);
|
||||
/* Aim to process all objects over at most 10 seconds.
|
||||
* To reduce the number of packets sent objects are
|
||||
* calculated ~twice a second. This means if the client had
|
||||
* 1000 objects visible to them they would need to process
|
||||
* 100 objects a second, so 50 objects every half a second.
|
||||
* This should be no problem even on slower mobile devices. */
|
||||
int iterations;
|
||||
//Normal update.
|
||||
if (!forceFullUpdate)
|
||||
{
|
||||
iterations = Mathf.Min(spawnedCount, estimatedMaximumIterations);
|
||||
}
|
||||
//Force does a full update.
|
||||
else
|
||||
{
|
||||
_nextLodNobIndex = 0;
|
||||
iterations = spawnedCount;
|
||||
}
|
||||
|
||||
//Cache a few more things.
|
||||
Dictionary<NetworkObject, byte> currentLods = Connection.LevelOfDetails;
|
||||
List<float> lodDistances = NetworkManager.ObserverManager.GetLevelOfDetailDistances();
|
||||
|
||||
//Index to use next is too high so reset it.
|
||||
if (_nextLodNobIndex >= spawnedCount)
|
||||
_nextLodNobIndex = 0;
|
||||
int nobIndex = _nextLodNobIndex;
|
||||
|
||||
PooledWriter tmpWriter = WriterPool.GetWriter(1000);
|
||||
int written = 0;
|
||||
|
||||
//Only check if player has objects.
|
||||
if (_objectsPositionsCache.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
NetworkObject nob = localClientSpawned[nobIndex];
|
||||
//Somehow went null. Can occur perhaps if client destroys objects between ticks maybe.
|
||||
if (nob == null)
|
||||
{
|
||||
IncreaseObjectIndex();
|
||||
continue;
|
||||
}
|
||||
//Only check objects not owned by the local client.
|
||||
if (!nob.IsOwner && !nob.IsDeinitializing)
|
||||
{
|
||||
Vector3 nobPosition = nob.transform.position;
|
||||
float closestDistance = float.MaxValue;
|
||||
foreach (Vector3 objPosition in _objectsPositionsCache)
|
||||
{
|
||||
float dist = Vector3.SqrMagnitude(nobPosition - objPosition);
|
||||
if (dist < closestDistance)
|
||||
closestDistance = dist;
|
||||
}
|
||||
|
||||
//If not within any distances then max lod will be used, the value below.
|
||||
byte lod = (byte)(lodDistances.Count - 1);
|
||||
for (byte z = 0; z < lodDistances.Count; z++)
|
||||
{
|
||||
//Distance is within range of this lod.
|
||||
if (closestDistance <= lodDistances[z])
|
||||
{
|
||||
lod = z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool changed;
|
||||
/* See if value changed. Value is changed
|
||||
* if it's not the same of old or if
|
||||
* the nob has not yet been added to the
|
||||
* level of details collection.
|
||||
* Even if a forced update only delta
|
||||
* needs to send. */
|
||||
if (currentLods.TryGetValue(nob, out byte oldLod))
|
||||
changed = (oldLod != lod);
|
||||
else
|
||||
changed = true;
|
||||
|
||||
//If changed then set new value and write.
|
||||
if (changed)
|
||||
{
|
||||
currentLods[nob] = lod;
|
||||
tmpWriter.WriteNetworkObjectId(nob.ObjectId);
|
||||
tmpWriter.WriteByte(lod);
|
||||
written++;
|
||||
}
|
||||
}
|
||||
|
||||
IncreaseObjectIndex();
|
||||
|
||||
void IncreaseObjectIndex()
|
||||
{
|
||||
nobIndex++;
|
||||
if (nobIndex >= spawnedCount)
|
||||
nobIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Set next lod index to current nob index.
|
||||
_nextLodNobIndex = nobIndex;
|
||||
/* Send using the reliable channel since
|
||||
* we are using deltas. This is also why
|
||||
* updates are sent larger chunked twice a second rather
|
||||
* than smaller chunks regularly. */
|
||||
PooledWriter writer = WriterPool.GetWriter(1000);
|
||||
writer.WritePacketId(PacketId.NetworkLODUpdate);
|
||||
writer.WriteInt32(written);
|
||||
writer.WriteArraySegment(tmpWriter.GetArraySegment());
|
||||
NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment(), true);
|
||||
|
||||
//Dispose writers.
|
||||
writer.DisposeLength();
|
||||
tmpWriter.DisposeLength();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user