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;
}
}
}