diff --git a/Assets/Mirror/Core/NetworkClient.cs b/Assets/Mirror/Core/NetworkClient.cs index a59dbb510..e536914e5 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -1991,7 +1991,6 @@ public static void OnGUI() GUILayout.Box($"timeline: {localTimeline:F2}"); GUILayout.Box($"buffer: {snapshots.Count}"); GUILayout.Box($"DriftEMA: {NetworkClient.driftEma.Value:F2}"); - GUILayout.Box($"DelTimeEMA: {NetworkClient.deliveryTimeEma.Value:F2}"); GUILayout.Box($"timescale: {localTimescale:F2}"); GUILayout.Box($"BTM: {NetworkClient.bufferTimeMultiplier:F2}"); // current dynamically adjusted multiplier GUILayout.Box($"RTT: {NetworkTime.rtt * 1000:F0}ms"); diff --git a/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs b/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs index 1080a5d4c..b00f7335a 100644 --- a/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs +++ b/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs @@ -47,34 +47,6 @@ public static partial class NetworkClient // ever add one value. static ExponentialMovingAverage driftEma; - // dynamic buffer time adjustment ////////////////////////////////////// - // dynamically adjusts bufferTimeMultiplier for smooth results. - // to understand how this works, try this manually: - // - // - disable dynamic adjustment - // - set jitter = 0.2 (20% is a lot!) - // - notice some stuttering - // - disable interpolation to see just how much jitter this really is(!) - // - enable interpolation again - // - manually increase bufferTimeMultiplier to 3-4 - // ... the cube slows down (blue) until it's smooth - // - with dynamic adjustment enabled, it will set 4 automatically - // ... the cube slows down (blue) until it's smooth as well - // - // note that 20% jitter is extreme. - // for this to be perfectly smooth, set the safety tolerance to '2'. - // but realistically this is not necessary, and '1' is enough. - [Header("Snapshot Interpolation: Dynamic Adjustment")] - [Tooltip("Automatically adjust bufferTimeMultiplier for smooth results.\nSets a low multiplier on stable connections, and a high multiplier on jittery connections.")] - public static bool dynamicAdjustment = true; - - [Tooltip("Safety buffer that is always added to the dynamic bufferTimeMultiplier adjustment.")] - public static float dynamicAdjustmentTolerance = 1; // 1 is realistically just fine, 2 is very very safe even for 20% jitter. can be half a frame too. (see above comments) - - [Tooltip("Dynamic adjustment is computed over n-second exponential moving average standard deviation.")] - public static int deliveryTimeEmaDuration = 2; // 1-2s recommended to capture average delivery time - static ExponentialMovingAverage deliveryTimeEma; // average delivery time (standard deviation gives average jitter) - // OnValidate: see NetworkClient.cs // add snapshot & initialize client interpolation time if needed @@ -91,7 +63,6 @@ static void InitTimeInterpolation() // 1 second holds 'sendRate' worth of values. // multiplied by emaDuration gives n-seconds. driftEma = new ExponentialMovingAverage(NetworkServer.sendRate * snapshotSettings.driftEmaDuration); - deliveryTimeEma = new ExponentialMovingAverage(NetworkServer.sendRate); } // server sends TimeSnapshotMessage every sendInterval. @@ -129,8 +100,7 @@ public static void OnTimeSnapshot(TimeSnapshot snap) snapshotSettings.slowdownSpeed, ref driftEma, snapshotSettings.catchupNegativeThreshold, - snapshotSettings.catchupPositiveThreshold, - ref deliveryTimeEma); + snapshotSettings.catchupPositiveThreshold); inserted= true; diff --git a/Assets/Mirror/Core/NetworkConnectionToClient.cs b/Assets/Mirror/Core/NetworkConnectionToClient.cs index b7ad09624..da40474b6 100644 --- a/Assets/Mirror/Core/NetworkConnectionToClient.cs +++ b/Assets/Mirror/Core/NetworkConnectionToClient.cs @@ -83,8 +83,7 @@ public void OnTimeSnapshot(TimeSnapshot snapshot) NetworkClient.snapshotSettings.slowdownSpeed, ref driftEma, NetworkClient.snapshotSettings.catchupNegativeThreshold, - NetworkClient.snapshotSettings.catchupPositiveThreshold, - ref deliveryTimeEma + NetworkClient.snapshotSettings.catchupPositiveThreshold ); } diff --git a/Assets/Mirror/Core/SnapshotInterpolation/SnapshotInterpolation.cs b/Assets/Mirror/Core/SnapshotInterpolation/SnapshotInterpolation.cs index 2d1a2fc55..27f57205a 100644 --- a/Assets/Mirror/Core/SnapshotInterpolation/SnapshotInterpolation.cs +++ b/Assets/Mirror/Core/SnapshotInterpolation/SnapshotInterpolation.cs @@ -152,8 +152,7 @@ public static void InsertAndAdjust( double slowdownSpeed, // in % [0,1] ref ExponentialMovingAverage driftEma, // for catchup / slowdown float catchupNegativeThreshold, // in % of sendInteral (careful, we may run out of snapshots) - float catchupPositiveThreshold, // in % of sendInterval - ref ExponentialMovingAverage deliveryTimeEma) // for dynamic buffer time adjustment + float catchupPositiveThreshold) // in % of sendInterval where T : Snapshot { // first snapshot? @@ -176,33 +175,6 @@ public static void InsertAndAdjust( // need to handle it silently. if (InsertIfNotExists(buffer, bufferLimit, snapshot)) { - // dynamic buffer adjustment needs delivery interval jitter - if (buffer.Count >= 2) - { - // note that this is not entirely accurate for scrambled inserts. - // - // we always use the last two, not what we just inserted - // even if we were to use the diff for what we just inserted, - // a scrambled insert would still not be 100% accurate: - // => assume a buffer of AC, with delivery time C-A - // => we then insert B, with delivery time B-A - // => but then technically the first C-A wasn't correct, - // as it would have to be C-B - // - // in practice, scramble is rare and won't make much difference - double previousLocalTime = buffer.Values[buffer.Count - 2].localTime; - double lastestLocalTime = buffer.Values[buffer.Count - 1].localTime; - - // this is the delivery time since last snapshot - double localDeliveryTime = lastestLocalTime - previousLocalTime; - - // feed the local delivery time to the EMA. - // this is what the original stream did too. - // our final dynamic buffer adjustment is different though. - // we use standard deviation instead of average. - deliveryTimeEma.Add(localDeliveryTime); - } - // adjust timescale to catch up / slow down after each insertion // because that is when we add new values to our EMA. diff --git a/Assets/Mirror/Examples/LagCompensation/ClientCube.cs b/Assets/Mirror/Examples/LagCompensation/ClientCube.cs index a337abe22..eae15727e 100644 --- a/Assets/Mirror/Examples/LagCompensation/ClientCube.cs +++ b/Assets/Mirror/Examples/LagCompensation/ClientCube.cs @@ -82,8 +82,7 @@ public void OnMessage(Snapshot3D snap) snapshotSettings.slowdownSpeed, ref driftEma, snapshotSettings.catchupNegativeThreshold, - snapshotSettings.catchupPositiveThreshold, - ref deliveryTimeEma); + snapshotSettings.catchupPositiveThreshold); } void Update() diff --git a/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs b/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs index 65ee3a977..992125062 100644 --- a/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs +++ b/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs @@ -85,8 +85,7 @@ public void OnMessage(Snapshot3D snap) snapshotSettings.slowdownSpeed, ref driftEma, snapshotSettings.catchupNegativeThreshold, - snapshotSettings.catchupPositiveThreshold, - ref deliveryTimeEma); + snapshotSettings.catchupPositiveThreshold); } void Update() diff --git a/Assets/Mirror/Tests/Editor/SnapshotInterpolation/SnapshotInterpolationTests.cs b/Assets/Mirror/Tests/Editor/SnapshotInterpolation/SnapshotInterpolationTests.cs index 83d1182c1..b27c19b71 100644 --- a/Assets/Mirror/Tests/Editor/SnapshotInterpolation/SnapshotInterpolationTests.cs +++ b/Assets/Mirror/Tests/Editor/SnapshotInterpolation/SnapshotInterpolationTests.cs @@ -181,8 +181,8 @@ public void InsertTwice() double localTimescale = 0; // insert twice - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, snap, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, snap, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, snap, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, snap, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); // should only be inserted once Assert.That(buffer.Count, Is.EqualTo(1)); @@ -203,8 +203,8 @@ public void Insert_Sorts() SimpleSnapshot b = new SimpleSnapshot(3, 0, 43); // insert in reverse order - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); // should be in sorted order Assert.That(buffer.Count, Is.EqualTo(2)); @@ -228,11 +228,11 @@ public void Insert_InitializesLocalTimeline() SimpleSnapshot b = new SimpleSnapshot(3, 0, 43); // first insertion should initialize the local timeline to remote time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time // second insertion should not modify the timeline again - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time } @@ -253,13 +253,13 @@ public void Insert_ComputesAverageDrift() SimpleSnapshot c = new SimpleSnapshot(5, 0, 43); // insert in order - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time // first insertion initializes localTime to '2'. @@ -285,13 +285,13 @@ public void Insert_ComputesAverageDrift_Scrambled() SimpleSnapshot c = new SimpleSnapshot(5, 0, 43); // insert scrambled (not in order) - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time // first insertion initializes localTime to '2'. @@ -322,13 +322,13 @@ public void Insert_ComputesAverageDeliveryInterval() SimpleSnapshot c = new SimpleSnapshot(5, 6, 43); // insert in order - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2 - bufferTime)); // initial snapshot - buffer time // first insertion doesn't compute delivery interval because we need 2 snaps. @@ -358,13 +358,13 @@ public void Insert_ComputesAverageDeliveryInterval_Scrambled() SimpleSnapshot c = new SimpleSnapshot(5, 6, 43); // insert in order - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2)); // detect wrong timeline immediately - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2)); // detect wrong timeline immediately - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, 0, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(2)); // detect wrong timeline immediately @@ -393,13 +393,13 @@ public void Sample() SimpleSnapshot b = new SimpleSnapshot(20, 0, 43); SimpleSnapshot c = new SimpleSnapshot(30, 0, 44); - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time // sample at a time before the first snapshot @@ -437,13 +437,13 @@ public void Step() SimpleSnapshot b = new SimpleSnapshot(20, 0, 43); SimpleSnapshot c = new SimpleSnapshot(30, 0, 44); - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time // step half way to the next snapshot @@ -468,13 +468,13 @@ public void Step_RemovesOld() SimpleSnapshot c = new SimpleSnapshot(30, 0, 44); double bufferTime = 30; // don't move timeline until all 3 inserted - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, a, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, b, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time - SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0, ref deliveryIntervalEma); + SnapshotInterpolation.InsertAndAdjust(buffer, bufferLimit, c, ref localTimeline, ref localTimescale, 0, bufferTime, 0.01, 0.01, ref driftEma, 0, 0); Assert.That(localTimeline, Is.EqualTo(10-bufferTime)); // initial snapshot - buffer time // step 1.5 snapshots worth, so way past the first one