From f3cb8f973f373c802790e150751bbd21be144f92 Mon Sep 17 00:00:00 2001 From: vis2k Date: Mon, 3 Oct 2022 10:37:34 +0200 Subject: [PATCH] wip --- Assets/Mirror/Core/NetworkClient.cs | 8 +++ .../Core/NetworkClient_TimeInterpolation.cs | 68 +++++++++++++++++++ Assets/Mirror/Core/NetworkServer.cs | 1 + 3 files changed, 77 insertions(+) diff --git a/Assets/Mirror/Core/NetworkClient.cs b/Assets/Mirror/Core/NetworkClient.cs index 1c7b398bb..17ffc9cd0 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -93,6 +93,9 @@ public struct NetworkClientConfig // call this from Unity's OnValidate public void OnValidate() { + // snapshot interpolation thresholds need to be <0 and >0 + catchupNegativeThreshold = Math.Min(catchupNegativeThreshold, 0); + catchupPositiveThreshold = Math.Max(catchupPositiveThreshold, 0); } } @@ -245,6 +248,7 @@ public static void Connect(string address) // Debug.Log($"Client Connect: {address}"); Debug.Assert(Transport.active != null, "There was no active transport when calling NetworkClient.Connect, If you are calling Connect manually then make sure to set 'Transport.active' first"); + InitTimeInterpolation(); RegisterSystemHandlers(false); Transport.active.enabled = true; AddTransportHandlers(); @@ -261,6 +265,7 @@ public static void Connect(Uri uri) // Debug.Log($"Client Connect: {uri}"); Debug.Assert(Transport.active != null, "There was no active transport when calling NetworkClient.Connect, If you are calling Connect manually then make sure to set 'Transport.active' first"); + InitTimeInterpolation(); RegisterSystemHandlers(false); Transport.active.enabled = true; AddTransportHandlers(); @@ -1485,6 +1490,9 @@ internal static void NetworkEarlyUpdate() // process all incoming messages first before updating the world if (Transport.active != null) Transport.active.ClientEarlyUpdate(); + + // time snapshot interpolation + UpdateTimeInterpolation(); } // NetworkLateUpdate called after any Update/FixedUpdate/LateUpdate diff --git a/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs b/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs index 29505cd0f..e70809a5f 100644 --- a/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs +++ b/Assets/Mirror/Core/NetworkClient_TimeInterpolation.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using UnityEngine; namespace Mirror { @@ -45,5 +46,72 @@ public static partial class NetworkClient // average delivery time (standard deviation gives average jitter) static ExponentialMovingAverage deliveryTimeEma; + + // OnValidate: see NetworkClient.cs + // add snapshot & initialize client interpolation time if needed + + // initialization called from Awake + static void InitTimeInterpolation() + { + // initialize EMA with 'emaDuration' seconds worth of history. + // 1 second holds 'sendRate' worth of values. + // multiplied by emaDuration gives n-seconds. + driftEma = new ExponentialMovingAverage(NetworkServer.config.sendRate * config.driftEmaDuration); + deliveryTimeEma = new ExponentialMovingAverage(NetworkServer.config.sendRate * config.deliveryTimeEmaDuration); + } + + // see comments at the top of this file + public static void OnTimeSnapshot(TimeSnapshot snap) + { + // set local timestamp (= when it was received on our end) + snap.localTime = Time.timeAsDouble; + + // (optional) dynamic adjustment + if (config.dynamicAdjustment) + { + // set bufferTime on the fly. + // shows in inspector for easier debugging :) + config.bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment( + NetworkServer.config.sendInterval, + deliveryTimeEma.StandardDeviation, + config.dynamicAdjustmentTolerance + ); + } + + // insert into the buffer & initialize / adjust / catchup + SnapshotInterpolation.InsertAndAdjust( + snapshots, + snap, + ref localTimeline, + ref localTimescale, + NetworkServer.config.sendInterval, + config.bufferTime, + config.catchupSpeed, + config.slowdownSpeed, + ref driftEma, + config.catchupNegativeThreshold, + config.catchupPositiveThreshold, + ref deliveryTimeEma); + + // Debug.Log($"inserted TimeSnapshot remote={snap.remoteTime:F2} local={snap.localTime:F2} total={snapshots.Count}"); + } + + // call this from early update, so the timeline is safe to use in update + static void UpdateTimeInterpolation() + { + // only while we have snapshots. + // timeline starts when the first snapshot arrives. + if (snapshots.Count > 0) + { + // progress local timeline. + SnapshotInterpolation.StepTime(Time.unscaledDeltaTime, ref localTimeline, localTimescale); + + // progress local interpolation. + // TimeSnapshot doesn't interpolate anything. + // this is merely to keep removing older snapshots. + SnapshotInterpolation.StepInterpolation(snapshots, localTimeline, out _, out _, out double t); + // Debug.Log($"NetworkClient SnapshotInterpolation @ {localTimeline:F2} t={t:F2}"); + } + } } } diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index 557b402aa..0a30df17b 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -24,6 +24,7 @@ public struct NetworkServerConfig tickRate < int.MaxValue ? 1f / tickRate : 0; // for 30 Hz, that's 33ms // Mirror currently sends every tick + public int sendRate => tickRate; public float sendInterval => tickInterval; // default settings in one place. used by both NetworkServer & Manager.