Skip to content

fix: LegacyLerp was using ticklatency as opposed to legacy ticksAgo #3381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;

namespace Unity.Netcode
Expand All @@ -14,9 +15,16 @@ public abstract class BufferedLinearInterpolator<T> where T : struct
// Constant absolute value for max buffer count instead of dynamic time based value. This is in case we have very low tick rates, so
// that we don't have a very small buffer because of this.
private const int k_BufferCountLimit = 100;
private const float k_AproximatePrecision = 0.0001f;
private const float k_ApproximateLowPrecision = 0.000001f;
private const float k_ApproximateHighPrecision = 1E-10f;
private const double k_SmallValue = 9.999999439624929E-11; // copied from Vector3's equal operator

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float GetPrecision()
{
return m_BufferQueue.Count == 0 ? k_ApproximateHighPrecision : k_ApproximateLowPrecision;
}

#region Legacy notes
// Buffer consumption scenarios
// Perfect case consumption
Expand Down Expand Up @@ -132,20 +140,23 @@ internal struct CurrentState
public float CurrentDeltaTime => m_CurrentDeltaTime;
public double FinalTimeToTarget => Math.Max(0.0, TimeToTargetValue - DeltaTime);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddDeltaTime(float deltaTime)
{
m_CurrentDeltaTime = deltaTime;
DeltaTime = Math.Min(DeltaTime + deltaTime, TimeToTargetValue);
LerpT = (float)(TimeToTargetValue == 0.0 ? 1.0 : DeltaTime / TimeToTargetValue);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetTimeToTarget(double timeToTarget)
{
LerpT = 0.0f;
DeltaTime = 0.0f;
TimeToTargetValue = timeToTarget;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TargetTimeAproximatelyReached()
{
if (!Target.HasValue)
Expand Down Expand Up @@ -237,6 +248,7 @@ public void ResetTo(T targetValue, double serverTime)
InternalReset(targetValue, serverTime);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InternalReset(T targetValue, double serverTime, bool addMeasurement = true)
{
m_RateOfChange = default;
Expand Down Expand Up @@ -271,7 +283,7 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
{
if (!InterpolateState.TargetReached)
{
InterpolateState.TargetReached = IsAproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item);
InterpolateState.TargetReached = IsApproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, GetPrecision());
}
return;
}
Expand All @@ -291,7 +303,7 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
potentialItemNeedsProcessing = ((potentialItem.TimeSent <= renderTime) && potentialItem.TimeSent > InterpolateState.Target.Value.TimeSent);
if (!InterpolateState.TargetReached)
{
InterpolateState.TargetReached = IsAproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item);
InterpolateState.TargetReached = IsApproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, GetPrecision());
}
}

Expand Down Expand Up @@ -424,6 +436,7 @@ internal T Update(float deltaTime, double tickLatencyAsTime, double minDeltaTime
/// This version of TryConsumeFromBuffer adheres to the original BufferedLinearInterpolator buffer consumption pattern.
/// </remarks>
/// <param name="renderTime"></param>
/// <param name="serverTime"></param>
private void TryConsumeFromBuffer(double renderTime, double serverTime)
{
if (!InterpolateState.Target.HasValue || (InterpolateState.Target.Value.TimeSent <= renderTime))
Expand All @@ -433,14 +446,16 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
while (m_BufferQueue.TryPeek(out BufferedItem potentialItem))
{
// If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
// to consume.
// to consume. (just a safety check but this scenario should never happen based on the below legacy approach of
// consuming until the most current state)
if (previousItem.HasValue && previousItem.Value.TimeSent == potentialItem.TimeSent)
{
break;
}

if ((potentialItem.TimeSent <= serverTime) &&
(!InterpolateState.Target.HasValue || InterpolateState.Target.Value.TimeSent < potentialItem.TimeSent))
// Continue to processing until we reach the most current state
if ((potentialItem.TimeSent <= serverTime) && // Inverted logic (below) from original since we have to go from past to present
(!InterpolateState.Target.HasValue || potentialItem.TimeSent > InterpolateState.Target.Value.TimeSent))
{
if (m_BufferQueue.TryDequeue(out BufferedItem target))
{
Expand All @@ -449,6 +464,7 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
InterpolateState.Target = target;
alreadyHasBufferItem = true;
InterpolateState.NextValue = InterpolateState.CurrentValue;
InterpolateState.PreviousValue = InterpolateState.CurrentValue;
InterpolateState.StartTime = target.TimeSent;
InterpolateState.EndTime = target.TimeSent;
}
Expand All @@ -458,19 +474,15 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
{
alreadyHasBufferItem = true;
InterpolateState.StartTime = InterpolateState.Target.Value.TimeSent;
InterpolateState.NextValue = InterpolateState.CurrentValue;
InterpolateState.PreviousValue = InterpolateState.NextValue;
InterpolateState.TargetReached = false;
}
InterpolateState.EndTime = target.TimeSent;
InterpolateState.Target = target;
InterpolateState.TimeToTargetValue = InterpolateState.EndTime - InterpolateState.StartTime;
InterpolateState.Target = target;
}
}
}
else
{
break;
}

if (!InterpolateState.Target.HasValue)
{
Expand Down Expand Up @@ -505,19 +517,20 @@ public T Update(float deltaTime, double renderTime, double serverTime)
InterpolateState.LerpT = Math.Clamp((float)((renderTime - InterpolateState.StartTime) / InterpolateState.TimeToTargetValue), 0.0f, 1.0f);
}

var target = Interpolate(InterpolateState.NextValue, InterpolateState.Target.Value.Item, InterpolateState.LerpT);
InterpolateState.NextValue = Interpolate(InterpolateState.PreviousValue, InterpolateState.Target.Value.Item, InterpolateState.LerpT);

if (LerpSmoothEnabled)
{
// Assure our MaximumInterpolationTime is valid and that the second lerp time ranges between deltaTime and 1.0f.
InterpolateState.CurrentValue = Interpolate(InterpolateState.CurrentValue, target, deltaTime / MaximumInterpolationTime);
InterpolateState.CurrentValue = Interpolate(InterpolateState.CurrentValue, InterpolateState.NextValue, deltaTime / MaximumInterpolationTime);
}
else
{
InterpolateState.CurrentValue = target;
InterpolateState.CurrentValue = InterpolateState.NextValue;
}

// Determine if we have reached our target
InterpolateState.TargetReached = IsAproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item);
InterpolateState.TargetReached = IsApproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, GetPrecision());
}
else // If the target is reached and we have no more state updates, we want to check to see if we need to reset.
if (m_BufferQueue.Count == 0 && InterpolateState.TargetReached)
Expand Down Expand Up @@ -601,6 +614,7 @@ public void AddMeasurement(T newMeasurement, double sentTime)
/// Gets latest value from the interpolator. This is updated every update as time goes by.
/// </summary>
/// <returns>The current interpolated value of type 'T'</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetInterpolatedValue()
{
return InterpolateState.CurrentValue;
Expand Down Expand Up @@ -638,6 +652,7 @@ public T GetInterpolatedValue()
/// <param name="deltaTime">The increasing delta time from when start to finish.</param>
/// <param name="maxSpeed">Maximum rate of change per pass.</param>
/// <returns>The smoothed <see cref="T"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected virtual T SmoothDamp(T current, T target, ref T rateOfChange, float duration, float deltaTime, float maxSpeed = Mathf.Infinity)
{
return target;
Expand All @@ -653,7 +668,8 @@ private protected virtual T SmoothDamp(T current, T target, ref T rateOfChange,
/// <param name="second">Second value of type <see cref="T"/>.</param>
/// <param name="precision">The precision of the aproximation.</param>
/// <returns>true if the two values are aproximately the same and false if they are not</returns>
private protected virtual bool IsAproximately(T first, T second, float precision = k_AproximatePrecision)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected virtual bool IsApproximately(T first, T second, float precision = k_ApproximateLowPrecision)
{
return false;
}
Expand All @@ -665,6 +681,7 @@ private protected virtual bool IsAproximately(T first, T second, float precision
/// <param name="item">The item to convert.</param>
/// <param name="inLocalSpace">local or world space (true or false).</param>
/// <returns>The converted value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected internal virtual T OnConvertTransformSpace(Transform transform, T item, bool inLocalSpace)
{
return default;
Expand All @@ -675,6 +692,7 @@ protected internal virtual T OnConvertTransformSpace(Transform transform, T item
/// </summary>
/// <param name="transform">The transform that the <see cref="Components.NetworkTransform"/> is associated with.</param>
/// <param name="inLocalSpace">Whether the <see cref="Components.NetworkTransform"/> is now being tracked in local or world spaced.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ConvertTransformSpace(Transform transform, bool inLocalSpace)
{
var count = m_BufferQueue.Count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected override float Interpolate(float start, float end, float time)

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected override bool IsAproximately(float first, float second, float precision = 1E-07F)
private protected override bool IsApproximately(float first, float second, float precision = 1E-06F)
{
return Mathf.Approximately(first, second);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private protected override Quaternion SmoothDamp(Quaternion current, Quaternion

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected override bool IsAproximately(Quaternion first, Quaternion second, float precision)
private protected override bool IsApproximately(Quaternion first, Quaternion second, float precision = 1E-06F)
{
return Mathf.Abs(first.x - second.x) <= precision &&
Mathf.Abs(first.y - second.y) <= precision &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected internal override Vector3 OnConvertTransformSpace(Transform transform,

/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected override bool IsAproximately(Vector3 first, Vector3 second, float precision = 0.0001F)
private protected override bool IsApproximately(Vector3 first, Vector3 second, float precision = 1E-06F)
{
return Math.Round(Mathf.Abs(first.x - second.x), 2) <= precision &&
Math.Round(Mathf.Abs(first.y - second.y), 2) <= precision &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,8 @@ public enum InterpolationTypes
/// <item><description>The first phase lerps from the previous state update value to the next state update value.</description></item>
/// <item><description>The second phase (optional) performs lerp smoothing where the current respective transform value is lerped towards the result of the first phase at a rate of delta time divided by the respective max interpolation time.</description></item>
/// </list>
/// !!! NOTE !!!<br />
/// The legacy lerp interpolation type does not use <see cref="NetworkTimeSystem.TickLatency"/> to determine the buffer depth. This is to preserve the same interpolation results when lerp smoothing is enabled.<br />
/// </summary>
/// <remarks>
/// For more information:<br />
Expand Down Expand Up @@ -4085,6 +4087,17 @@ private void UpdateInterpolation()
}
}

// Note: This is for the legacy lerp type in order to maintain the same end result for any games under development that have tuned their
// project's to match the legacy lerp's end result. This will not allow changes
var cachedRenderTime = 0.0;
if (PositionInterpolationType == InterpolationTypes.LegacyLerp || RotationInterpolationType == InterpolationTypes.LegacyLerp || ScaleInterpolationType == InterpolationTypes.LegacyLerp)
{
// Since InterpolationBufferTickOffset defaults to zero, this should not impact exist projects but still provides users with the ability to tweak
// their ticks ago time.
var ticksAgo = (!IsServerAuthoritative() && !IsServer ? 2 : 1) + InterpolationBufferTickOffset;
cachedRenderTime = timeSystem.TimeTicksAgo(ticksAgo).Time;
}

// Get the tick latency (ticks ago) as time (in the past) to process state updates in the queue.
var tickLatencyAsTime = timeSystem.TimeTicksAgo(tickLatency).Time;

Expand Down Expand Up @@ -4127,7 +4140,7 @@ private void UpdateInterpolation()

if (PositionInterpolationType == InterpolationTypes.LegacyLerp)
{
m_PositionInterpolator.Update(cachedDeltaTime, tickLatencyAsTime, currentTime);
m_PositionInterpolator.Update(cachedDeltaTime, cachedRenderTime, currentTime);
}
else
{
Expand Down Expand Up @@ -4158,7 +4171,7 @@ private void UpdateInterpolation()
m_RotationInterpolator.IsSlerp = !UseHalfFloatPrecision;
if (RotationInterpolationType == InterpolationTypes.LegacyLerp)
{
m_RotationInterpolator.Update(cachedDeltaTime, tickLatencyAsTime, currentTime);
m_RotationInterpolator.Update(cachedDeltaTime, cachedRenderTime, currentTime);
}
else
{
Expand Down Expand Up @@ -4186,7 +4199,7 @@ private void UpdateInterpolation()

if (ScaleInterpolationType == InterpolationTypes.LegacyLerp)
{
m_ScaleInterpolator.Update(cachedDeltaTime, tickLatencyAsTime, currentTime);
m_ScaleInterpolator.Update(cachedDeltaTime, cachedRenderTime, currentTime);
}
else
{
Expand Down