Unity-WebSocket/Assets/FishNet/Runtime/Connection/EstimatedTick.cs
2025-06-28 11:28:54 +03:30

217 lines
7.7 KiB
C#

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