Skip to content

Commit f505331

Browse files
fix: Return a duration from NetworkTransform.GetTickLatencyInSeconds
GetTickLatencyInSeconds returned TimeTicksAgo(...).Time, which is an absolute network timestamp rather than a duration, so the value grew for as long as the session ran. It is documented as returning the tick latency in seconds, and NetworkTimeSystem.TickLatency points at it as a way to inspect that latency, so the contract was misleading regardless of which clock it was measured from. It now returns the tick count multiplied by the tick interval. This also takes the clock question out of this method entirely, since a duration does not reference LocalTime or ServerTime. The change to derive interpolation render time from ServerTime now applies only to UpdateInterpolation. Adds integration tests covering the documented contract: the value tracks the tick latency rather than elapsed time, and lengthens by exactly the tick interval for each tick of additional buffering. Both fail against the previous implementation, the second regardless of how long the session has run, since buffering more ticks used to make the reported latency smaller.
1 parent fdb832e commit f505331

4 files changed

Lines changed: 103 additions & 2 deletions

File tree

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2323
### Fixed
2424

2525
- Issue where `NetworkTransform` interpolated towards a point in time taken from the local clock rather than the server clock that state updates are stamped on, which starved the interpolator on clients and reduced interpolation to snapping between state updates. (#4133)
26-
- Issue where `NetworkTransform.GetTickLatencyInSeconds` returned a time based on the local clock instead of the server clock used for interpolation. (#4133)
26+
- Issue where `NetworkTransform.GetTickLatencyInSeconds` returned an absolute network timestamp that grew for as long as the session ran, rather than the tick latency as a duration in seconds that it is documented to return. (#4133)
2727

2828
### Security
2929

com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4700,7 +4700,10 @@ internal static float GetTickLatencyInSeconds(NetworkManager networkManager)
47004700
{
47014701
if (networkManager.IsListening)
47024702
{
4703-
return (float)networkManager.ServerTime.TimeTicksAgo(networkManager.NetworkTimeSystem.TickLatency + InterpolationBufferTickOffset).Time;
4703+
// The number of ticks the interpolators run behind, as a duration. This is not a point in time:
4704+
// it does not grow as the session runs.
4705+
var ticksBehind = networkManager.NetworkTimeSystem.TickLatency + InterpolationBufferTickOffset;
4706+
return (float)(ticksBehind * networkManager.ServerTime.FixedDeltaTimeAsDouble);
47044707
}
47054708
return 0f;
47064709
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using Unity.Netcode.Components;
4+
using Unity.Netcode.TestHelpers.Runtime;
5+
using UnityEngine;
6+
using UnityEngine.TestTools;
7+
8+
namespace Unity.Netcode.RuntimeTests
9+
{
10+
/// <summary>
11+
/// Validates that <see cref="NetworkTransform.GetTickLatencyInSeconds()"/> returns what it is documented to
12+
/// return: the tick latency as a duration in seconds.
13+
/// </summary>
14+
/// <remarks>
15+
/// It previously returned <c>TimeTicksAgo(...).Time</c>, which is an absolute network timestamp rather than a
16+
/// duration, so the value grew for as long as the session ran.
17+
/// </remarks>
18+
internal class NetworkTransformTickLatencyTests : NetcodeIntegrationTest
19+
{
20+
protected override int NumberOfClients => 1;
21+
22+
// Ticks of additional buffering applied part way through the test to confirm the returned duration
23+
// tracks the tick latency it is derived from.
24+
private const int k_AddedBufferTicks = 3;
25+
26+
// Seconds of tolerance when comparing against the expected duration.
27+
private const float k_Tolerance = 0.0005f;
28+
29+
// The number of samples taken while the session runs, to confirm the value does not drift with time.
30+
private const int k_Samples = 30;
31+
32+
private int m_OriginalBufferTickOffset;
33+
34+
protected override IEnumerator OnSetup()
35+
{
36+
m_OriginalBufferTickOffset = NetworkTransform.InterpolationBufferTickOffset;
37+
return base.OnSetup();
38+
}
39+
40+
protected override IEnumerator OnTearDown()
41+
{
42+
// This is static, so leaving it modified would leak into every test that runs afterwards.
43+
NetworkTransform.InterpolationBufferTickOffset = m_OriginalBufferTickOffset;
44+
return base.OnTearDown();
45+
}
46+
47+
private static float GetExpectedLatencyInSeconds(NetworkManager networkManager)
48+
{
49+
var ticksBehind = networkManager.NetworkTimeSystem.TickLatency + NetworkTransform.InterpolationBufferTickOffset;
50+
return (float)(ticksBehind * networkManager.ServerTime.FixedDeltaTimeAsDouble);
51+
}
52+
53+
[UnityTest]
54+
public IEnumerator GetTickLatencyInSecondsReturnsADuration()
55+
{
56+
var client = m_ClientNetworkManagers[0];
57+
58+
// Sample repeatedly while the session clock advances. A duration tracks the tick latency and stays
59+
// put, where an absolute timestamp would climb by roughly one second per second.
60+
var firstSample = NetworkTransform.GetTickLatencyInSeconds(client);
61+
for (int i = 0; i < k_Samples; i++)
62+
{
63+
var expected = GetExpectedLatencyInSeconds(client);
64+
var actual = NetworkTransform.GetTickLatencyInSeconds(client);
65+
Assert.AreEqual(expected, actual, k_Tolerance,
66+
$"Expected the tick latency to be {expected}s but it was {actual}s.");
67+
yield return null;
68+
}
69+
70+
var lastSample = NetworkTransform.GetTickLatencyInSeconds(client);
71+
Assert.AreEqual(firstSample, lastSample, k_Tolerance,
72+
$"The tick latency changed from {firstSample}s to {lastSample}s while the session ran without " +
73+
$"the tick latency itself changing, so it is tracking elapsed time rather than latency.");
74+
}
75+
76+
[UnityTest]
77+
public IEnumerator GetTickLatencyInSecondsTracksTheBufferTickOffset()
78+
{
79+
var client = m_ClientNetworkManagers[0];
80+
var tickInterval = (float)client.ServerTime.FixedDeltaTimeAsDouble;
81+
82+
var before = NetworkTransform.GetTickLatencyInSeconds(client);
83+
84+
// Buffering more ticks has to lengthen the reported duration by exactly those ticks.
85+
NetworkTransform.InterpolationBufferTickOffset = m_OriginalBufferTickOffset + k_AddedBufferTicks;
86+
yield return null;
87+
88+
var after = NetworkTransform.GetTickLatencyInSeconds(client);
89+
var expectedIncrease = k_AddedBufferTicks * tickInterval;
90+
Assert.AreEqual(expectedIncrease, after - before, k_Tolerance,
91+
$"Adding {k_AddedBufferTicks} ticks of buffering changed the reported latency by " +
92+
$"{after - before}s when a tick is {tickInterval}s, so it should have changed by " +
93+
$"{expectedIncrease}s.");
94+
}
95+
}
96+
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTickLatencyTests.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)