using FishNet.Managing; using FishNet.Managing.Predicting; using FishNet.Managing.Timing; using FishNet.Serializing; using FishNet.Transporting; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; namespace FishNet.Connection { /// /// A container for a connected client used to perform actions on and gather information for the declared client. /// public partial class NetworkConnection { /// /// Average number of replicates in queue for the past x received replicates. /// private MovingAverage _replicateQueueAverage; /// /// Last tick replicateQueueAverage was updated. /// private uint _lastAverageQueueAddTick; internal void Prediction_Initialize(NetworkManager manager, bool asServer) { if (asServer) { int movingAverageCount = (int)Mathf.Max((float)manager.TimeManager.TickRate * 0.25f, 3f); _replicateQueueAverage = new MovingAverage(movingAverageCount); } } /// /// Adds to the average number of queued replicates. /// internal void AddAverageQueueCount(ushort value, uint tick) { /* If have not added anything to the averages for several ticks * then reset average. */ if ((tick - _lastAverageQueueAddTick) > _replicateQueueAverage.SampleSize) _replicateQueueAverage.Reset(); _lastAverageQueueAddTick = tick; _replicateQueueAverage.ComputeAverage((float)value); } /// /// Returns the highest queue count after resetting it. /// /// internal ushort GetAndResetAverageQueueCount() { if (_replicateQueueAverage == null) return 0; int avg = (int)(_replicateQueueAverage.Average); if (avg < 0) avg = 0; return (ushort)avg; } #if !PREDICTION_V2 /// /// Local tick when the connection last replicated. /// public uint LocalReplicateTick { get; internal set; } /// /// Resets NetworkConnection. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Prediction_Reset() { GetAndResetAverageQueueCount(); } #else /// /// Approximate replicate tick on the server for this connection. /// This also contains the last set value for local and remote. /// public EstimatedTick ReplicateTick; /// /// Writers for states. /// internal List PredictionStateWriters = new List(); /// /// Writes a prediction state. /// /// internal void WriteState(PooledWriter writer) { #if !DEVELOPMENT_BUILD && !UNITY_EDITOR //Do not send states to clientHost. if (IsLocalClient) return; #endif TimeManager tm = NetworkManager.TimeManager; uint ticksBehind = (IsLocalClient) ? 0 : PacketTick.LocalTickDifference(tm); //if (ticksBehind > 0) // return; /* If it's been a really long while the client could just be setting up * or dropping. Only send if they've communicated within 15 seconds. */ if (ticksBehind > (tm.TickRate * 15)) return; int mtu = NetworkManager.TransportManager.GetMTU((byte)Channel.Unreliable); PooledWriter stateWriter; int writerCount = PredictionStateWriters.Count; if (writerCount == 0 || (writer.Length + PredictionManager.STATE_HEADER_RESERVE_COUNT) > mtu) { stateWriter = WriterPool.Retrieve(mtu); PredictionStateWriters.Add(stateWriter); stateWriter.Reserve(PredictionManager.STATE_HEADER_RESERVE_COUNT); uint clientReplicateTick; //If client has performed a replicate. if (!ReplicateTick.IsUnset) clientReplicateTick = ReplicateTick.Value(NetworkManager.TimeManager); /* If not then use what is estimated to be the clients * current tick along with desired prediction queue count. * This should be just about the same as if the client used replicate, * but even if it's not it doesn't matter because the client * isn't replicating himself, just reconciling and replaying other objects. */ else clientReplicateTick = (PacketTick.Value(NetworkManager.TimeManager) + NetworkManager.PredictionManager.QueuedInputs); //Estimated replicate tick on the client. stateWriter.WriteTickUnpacked(clientReplicateTick); /* No need to send localTick here, it can be read from LastPacketTick that's included with every packet. * Note: the LastPacketTick we're sending here is the last packet received from this connection. * The server and client ALWAYS prefix their packets with their local tick, which is * what we are going to use for the last packet tick from the server. */ } else { stateWriter = PredictionStateWriters[writerCount - 1]; } stateWriter.WriteArraySegment(writer.GetArraySegment()); } /// /// Stores prediction writers to be re-used later. /// internal void StorePredictionStateWriters() { for (int i = 0; i < PredictionStateWriters.Count; i++) WriterPool.Store(PredictionStateWriters[i]); PredictionStateWriters.Clear(); } /// /// Resets NetworkConnection. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Prediction_Reset() { GetAndResetAverageQueueCount(); StorePredictionStateWriters(); ReplicateTick.Reset(); } #endif } }