diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 9829470469..4c40929b0a 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -22,6 +22,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Issue where `NetworkTime` reported a `TickOffset` of almost a whole tick, instead of zero, for some times that land on a tick (for example 0.3 at a tick rate of 10), which put `TickWithPartial` one tick ahead or, in long sessions, `Tick` one tick behind. Times between minus one tick and zero now report tick -1 with a positive `TickOffset`, as other negative times do. (#4227) - Issue where a `FastBufferReader` created with an offset and the default length of -1 read past the end of its source. The `ArraySegment` overload that takes an offset now applies it from the start of the segment. (#4221) - Issue where a client synchronized while a `NetworkList` still had unsent changes applied those changes twice, resulting in duplicate entries and index-based events landing on the wrong elements on that client. (#4215) - Issue where showing a `NetworkObject` with `NetworkShow` while its `NetworkVariable` updates were held back by `NetworkVariableUpdateTraits` stopped those pending changes from being sent until the variable was modified again. (#4215) diff --git a/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTime.cs b/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTime.cs index e452bbb187..975439d524 100644 --- a/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTime.cs +++ b/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTime.cs @@ -136,20 +136,18 @@ public NetworkTime TimeTicksAgo(int ticks, float offset = 0.0f) private void UpdateCache() { double d = m_TimeSec / m_TickInterval; - m_CachedTick = (int)d; - // This check is needed due to double division imprecision of large numbers - if ((d - m_CachedTick) >= 0.999999999999) + // Floor keeps the offset positive for negative time. + double tick = Math.Floor(d); + double fraction = d - tick; + // Double division imprecision can land a whole tick just below its value (0.3 / 0.1 = 2.9999999999999996), + // and the error grows with the tick count. + if (fraction >= 1d - Math.Max(1e-12, Math.Abs(d) * 1e-13)) { - m_CachedTick++; - } - m_CachedTickOffset = ((d - Math.Truncate(d)) * m_TickInterval); - - // This handles negative time, decreases tick by 1 and makes offset positive. - if (m_CachedTick < 0 && m_CachedTickOffset != 0d) - { - m_CachedTick--; - m_CachedTickOffset = m_TickInterval + m_CachedTickOffset; + tick++; + fraction = 0d; } + m_CachedTick = (int)tick; + m_CachedTickOffset = fraction * m_TickInterval; } /// diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs index 2a137bde2b..2612d859ff 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs @@ -66,6 +66,23 @@ public void NetworkTimeCreate(double time, double tickOffset, double epsilon = 0 Assert.IsTrue(Approximately(networkTime.TickOffset, tickOffset)); } + [Test] + [TestCase(0.25d, 2, 0.05d)] + [TestCase(0.3d, 3, 0d)] + [TestCase(0.7d, 7, 0d)] + [TestCase(-0.05d, -1, 0.05d)] + [TestCase(-0.15d, -2, 0.05d)] + [TestCase(-0.3d, -3, 0d)] + [TestCase(16384.3d, 163843, 0d)] + public void NetworkTimeTickAndOffset(double time, int expectedTick, double expectedTickOffset) + { + var networkTime = new NetworkTime(10, time); + + Assert.AreEqual(expectedTick, networkTime.Tick); + Assert.IsTrue(Approximately(networkTime.TickOffset, expectedTickOffset)); + Assert.IsTrue(Approximately(networkTime.FixedTime + networkTime.TickOffset, time)); + } + [Test] public void NetworkTimeDefault() {