using System.Runtime.CompilerServices; namespace FishNet.Managing.Timing { public class EstimatedTick { #region Types. /// /// How to handle old ticks, specifically related to EstimatedTick. /// public enum OldTickOption : byte { /// /// Completely ignore old ticks. /// Discard = 0, /// /// Set LastRemoteTick but do not update RemoteTick. /// SetLastRemoteTick = 1, /// /// Set LastRemoteTick and RemoteTick. /// SetRemoteTick = 2, } #endregion /// /// Local tick when this was last updated. /// public uint LocalTick { get; private set; } = TimeManager.UNSET_TICK; /// /// Last remote tick this was updated with that was not out of order or a duplicate. /// public uint RemoteTick { get; private set; } = TimeManager.UNSET_TICK; /// /// Last remote tick received regardless if it was out of order or a duplicate. /// public uint LastRemoteTick { get; private set; } = TimeManager.UNSET_TICK; /// /// True if LastRemoteTick is equal to RemoteTick. /// This would indicate that the LastRemoteTick did not arrive out of order. /// public bool IsLastRemoteTickOrdered => (LastRemoteTick == RemoteTick); /// /// True if value is unset. /// //Only need to check one value for unset as they all would be if not set. public bool IsUnset => (LocalTick == TimeManager.UNSET_TICK); /// /// Last TimeManager specified during an Update call. /// private TimeManager _updateTimeManager; /// /// LocalTick when Value was last reset. /// private uint _valueLocalTick = TimeManager.UNSET_TICK; /// /// Number of ticks LocalTick is being current LocalTick. /// public uint LocalTickDifference(TimeManager tm = null) { if (!TryAssignTimeManager(ref tm)) return TimeManager.UNSET_TICK; long value = (tm.LocalTick - LocalTick); //Shouldn't be possible to be less than 0. if (value < 0) return TimeManager.UNSET_TICK; else if (value > uint.MaxValue) value = uint.MaxValue; return (uint)value; } /// /// True if values were updated this tick. /// public bool IsCurrent(TimeManager tm = null) { if (!TryAssignTimeManager(ref tm)) return false; return (!IsUnset && LocalTick == tm.LocalTick); } /// /// Current estimated value. /// /// NetworkManager to use. When null default value will be returned. public uint Value(TimeManager tm = null) { if (!TryAssignTimeManager(ref tm)) return TimeManager.UNSET_TICK; return Value(out _, tm); } /// /// Current estimated value. Outputs if value is current. /// /// NetworkManager to use. When null default value will be returned. /// True if the value was updated this local tick. public uint Value(out bool isCurrent, TimeManager tm = null) { //Default value. isCurrent = false; if (!TryAssignTimeManager(ref tm)) return TimeManager.UNSET_TICK; if (IsUnset) return TimeManager.UNSET_TICK; isCurrent = IsCurrent(tm); uint diff = (tm.LocalTick - _valueLocalTick); return (diff + RemoteTick); } /// /// Initializes this EstimatedTick with values. /// public void Initialize(TimeManager tm, uint remoteTick = 0, uint lastRemoteTick = 0, uint localTick = 0) { _updateTimeManager = tm; RemoteTick = remoteTick; LastRemoteTick = lastRemoteTick; LocalTick = localTick; } /// /// Updates values. /// /// TimeManager to use. /// Remote tick being updated. /// How to handle remoteTick if it is old. /// /// True to reset Value based on this information. False will allow Value to continue to to estimate tick based on the last reset. /// True if was able to update values. public bool Update(TimeManager tm, uint remoteTick, OldTickOption oldTickOption = OldTickOption.Discard, bool resetValue = true) { _updateTimeManager = tm; //Always set LastRemoteTick even if out of order. LastRemoteTick = remoteTick; //If cannot update with old values return. if (oldTickOption != OldTickOption.SetRemoteTick && remoteTick <= RemoteTick) return false; //nm is assumed set here. LocalTick = tm.LocalTick; if (resetValue) _valueLocalTick = LocalTick; RemoteTick = remoteTick; return true; } /// /// Updates values. /// /// Remote tick being updated. /// How to handle remoteTick if it is old. /// True to reset Value based on this information. False will allow Value to continue to to estimate tick based on the last reset. /// True if was able to update values. public bool Update(uint remoteTick, OldTickOption oldTickOption = OldTickOption.Discard, bool resetValue = true) { TimeManager tm = null; if (!TryAssignTimeManager(ref tm)) return false; return Update(tm, remoteTick, oldTickOption); } /// /// Updates Value based on current ticks. /// This is typically used when you want to control when Value is reset through the Update methods. /// public void UpdateValue() { _valueLocalTick = LocalTick; } /// /// Assigns a TimeManager reference to UpdateTimeManager if was null. /// /// True if the reference has value or was assigned value. False if the reference remains null. private bool TryAssignTimeManager(ref TimeManager tm) { if (tm == null) tm = _updateTimeManager; return (tm != null); } /// /// Resets values to unset and clears the NetworkManager. /// public void Reset() { ResetTicks(); _updateTimeManager = null; } /// /// Resets only tick values, leaving type references. /// public void ResetTicks() { LocalTick = TimeManager.UNSET_TICK; RemoteTick = TimeManager.UNSET_TICK; LastRemoteTick = TimeManager.UNSET_TICK; _valueLocalTick = TimeManager.UNSET_TICK; } } }