diff --git a/Assets/Mirror/Core/AckDeltaCompression/AckDeltaCompression.cs b/Assets/Mirror/Core/AckDeltaCompression/AckDeltaCompression.cs index 0af7d5a4f..c66834878 100644 --- a/Assets/Mirror/Core/AckDeltaCompression/AckDeltaCompression.cs +++ b/Assets/Mirror/Core/AckDeltaCompression/AckDeltaCompression.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using UnityEngine; namespace Mirror { @@ -71,5 +72,30 @@ internal static void TrackIdentityAtTick( // add the netid to the hashset netIds.Add(netId); } + + // when receiving an ack from a connection, update latest ack for + // all networkidentities that were in the acked batch. + internal static void UpdateIdentityAcks( + double timestamp, + SortedList> identityTicks, + Dictionary identityAcks) + { + // find the identities that were in the acked batch + if (!identityTicks.TryGetValue(timestamp, out HashSet identities)) + { + // for now, at least log a message so we know this happened. + Debug.Log($"UpdateLatestAck: batch @ {timestamp} was not in history anymore. This can happen if the other end was too far behind."); + return; + } + + // update latest acks for all identities that were in the batch + foreach (uint netId in identities) + { + // unreliable messages may arrive out of order. + // only update if newer. + if (!identityAcks.TryGetValue(netId, out double ackTimestamp) || timestamp > ackTimestamp) + identityAcks[netId] = timestamp; + } + } } } diff --git a/Assets/Mirror/Core/NetworkConnection.cs b/Assets/Mirror/Core/NetworkConnection.cs index 4c1a77505..e5036dbbf 100644 --- a/Assets/Mirror/Core/NetworkConnection.cs +++ b/Assets/Mirror/Core/NetworkConnection.cs @@ -70,7 +70,7 @@ public abstract class NetworkConnection // currently we sync one entity at a time in batches, so we also need to store acks for one at a time. // // TODO remove old when unspawned - readonly Dictionary identityAcks = new Dictionary(); + internal readonly Dictionary identityAcks = new Dictionary(); // diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index 4df7e7545..22b443b58 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -497,7 +497,10 @@ static void OnTimeSnapshotMessage(NetworkConnectionToClient connection, TimeSnap // ack delta compression /////////////////////////////////////////////// static void OnAckMessage(NetworkConnectionToClient connection, AckMessage message) { - Debug.Log($"NetworkServer received acknowledgement: {message.batchTimestamp} for connId={connection.connectionId}"); + // for the acknowledged batch's timestamp: + // update last ack for all NetworkIdentities that were in the batch. + // Debug.Log($"NetworkServer received acknowledgement: {message.batchTimestamp} for connId={connection.connectionId}"); + AckDeltaCompression.UpdateIdentityAcks(message.batchTimestamp, connection.identityTicks, connection.identityAcks); } // connections ///////////////////////////////////////////////////////// diff --git a/Assets/Mirror/Tests/Editor/AckDeltaCompression/AckDeltaCompressionTests.cs b/Assets/Mirror/Tests/Editor/AckDeltaCompression/AckDeltaCompressionTests.cs index 7f13633ff..aa4a97d90 100644 --- a/Assets/Mirror/Tests/Editor/AckDeltaCompression/AckDeltaCompressionTests.cs +++ b/Assets/Mirror/Tests/Editor/AckDeltaCompression/AckDeltaCompressionTests.cs @@ -90,5 +90,48 @@ public void TrackIdentityAtTick() Assert.That(identityTicks[4.0].Contains(1337)); Assert.That(identityTicks[4.0].Contains(101)); } + + [Test] + public void UpdateIdentityAcks() + { + // prepare a few batches that were sent, with NetworkIdentities included + SortedList> identityTicks = new SortedList>(); + int MaxCount = 3; + + // insert t = 1 with a few netids + AckDeltaCompression.TrackIdentityAtTick(1.0, 42, identityTicks, MaxCount); + AckDeltaCompression.TrackIdentityAtTick(1.0, 1337, identityTicks, MaxCount); + + // insert t = 2 with a the same netIds and one new + AckDeltaCompression.TrackIdentityAtTick(2.0, 42, identityTicks, MaxCount); + AckDeltaCompression.TrackIdentityAtTick(2.0, 1337, identityTicks, MaxCount); + AckDeltaCompression.TrackIdentityAtTick(2.0, 101, identityTicks, MaxCount); + + // insert t = 3 without one of the previous netIds + AckDeltaCompression.TrackIdentityAtTick(3.0, 1337, identityTicks, MaxCount); + AckDeltaCompression.TrackIdentityAtTick(3.0, 101, identityTicks, MaxCount); + + // remote acks t = 1: 42, 1337 + Dictionary identityAcks = new Dictionary(); + AckDeltaCompression.UpdateIdentityAcks(1.0, identityTicks, identityAcks); + Assert.That(identityAcks.Count, Is.EqualTo(2)); + Assert.That(identityAcks[42], Is.EqualTo(1.0)); + Assert.That(identityAcks[1337], Is.EqualTo(1.0)); + + // remote acks t = 3 before t = 2: 1337, 101 + AckDeltaCompression.UpdateIdentityAcks(3.0, identityTicks, identityAcks); + Assert.That(identityAcks.Count, Is.EqualTo(3)); + Assert.That(identityAcks[42], Is.EqualTo(1.0)); // still from the first ack + Assert.That(identityAcks[1337], Is.EqualTo(3.0)); // acked + Assert.That(identityAcks[101], Is.EqualTo(3.0)); // acked + + // remote acks t = 2: 42, 1337, 101. + // only 42 is should be updated since 1337 and 101 are already newer + AckDeltaCompression.UpdateIdentityAcks(2.0, identityTicks, identityAcks); + Assert.That(identityAcks.Count, Is.EqualTo(3)); + Assert.That(identityAcks[42], Is.EqualTo(2.0)); // updated + Assert.That(identityAcks[1337], Is.EqualTo(3.0)); // already newer + Assert.That(identityAcks[101], Is.EqualTo(3.0)); // already newer + } } }