forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			198 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
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();
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
}
 |