diff --git a/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs b/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs index 9d948a4b520..b036293595e 100644 --- a/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs +++ b/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs @@ -449,6 +449,8 @@ public virtual void Reset() // default to ClientToServer so this works immediately for users syncDirection = SyncDirection.ClientToServer; + NetworkTime.highPingComponents = 0UL; + // default to 20Hz, 20 sends per second if data has changed. syncInterval = 0.05f; } @@ -456,6 +458,7 @@ public virtual void Reset() protected virtual void OnEnable() { ResetState(); + NetworkTime.highPingComponents++; if (NetworkServer.active) NetworkIdentity.clientAuthorityCallback += OnClientAuthorityChanged; @@ -465,6 +468,9 @@ protected virtual void OnDisable() { ResetState(); + if (NetworkTime.highPingComponents > 0UL) + NetworkTime.highPingComponents--; + if (NetworkServer.active) NetworkIdentity.clientAuthorityCallback -= OnClientAuthorityChanged; } diff --git a/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs b/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs index 51188478068..d3ade64170c 100644 --- a/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs +++ b/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs @@ -145,6 +145,17 @@ protected virtual void Awake() positionCorrectionThresholdSqr = positionCorrectionThreshold * positionCorrectionThreshold; } + protected virtual void OnEnable() + { + NetworkTime.highPingComponents++; + } + + protected virtual void OnDisable() + { + if (NetworkTime.highPingComponents > 0UL) + NetworkTime.highPingComponents--; + } + protected virtual void CopyRenderersAsGhost(GameObject destination, Material material) { // find the MeshRenderer component, which sometimes is on a child. diff --git a/Assets/Mirror/Core/NetworkClient.cs b/Assets/Mirror/Core/NetworkClient.cs index d20e7c07ff5..99bb8fda956 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -1793,7 +1793,8 @@ static void Broadcast(bool unreliableBaselineElapsed) if (NetworkServer.active) return; // send time snapshot every sendInterval. - Send(new TimeSnapshotMessage(), Channels.Unreliable); + if (NetworkTime.highPingComponents > 0UL) + Send(new TimeSnapshotMessage(), Channels.Unreliable); // broadcast client state to server BroadcastToServer(unreliableBaselineElapsed); diff --git a/Assets/Mirror/Core/NetworkConnectionToClient.cs b/Assets/Mirror/Core/NetworkConnectionToClient.cs index d80749c13ed..8e04cf9671c 100644 --- a/Assets/Mirror/Core/NetworkConnectionToClient.cs +++ b/Assets/Mirror/Core/NetworkConnectionToClient.cs @@ -136,6 +136,8 @@ protected virtual void UpdatePing() // localTime (double) instead of Time.time for accuracy over days if (NetworkTime.localTime >= lastPingTime + NetworkTime.PingInterval) { + //Debug.Log("NetworkConnectionToClient SendPing"); + // TODO it would be safer for the server to store the last N // messages' timestamp and only send a message number. // This way client's can't just modify the timestamp. diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index a7640dfbe75..a560d32f7f5 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -2281,7 +2281,8 @@ static void Broadcast(bool unreliableBaselineElapsed) // make sure Broadcast() is only called every sendInterval, // even if targetFrameRate isn't set in host mode (!) // (done via AccurateInterval) - connection.Send(new TimeSnapshotMessage(), Channels.Unreliable); + if (NetworkTime.highPingComponents > 0UL) + connection.Send(new TimeSnapshotMessage(), Channels.Unreliable); // broadcast world state to this connection BroadcastToConnection(connection, unreliableBaselineElapsed); diff --git a/Assets/Mirror/Core/NetworkTime.cs b/Assets/Mirror/Core/NetworkTime.cs index 6319970c45b..02de2609095 100644 --- a/Assets/Mirror/Core/NetworkTime.cs +++ b/Assets/Mirror/Core/NetworkTime.cs @@ -16,11 +16,17 @@ namespace Mirror /// Synchronizes server time to clients. public static class NetworkTime { + // Incremented / decremented by components that need higher ping frequency. + // Also used to determine if we need to send TimeSnapshotMessage by + // NetworkServer and NetworkClient. + internal static ulong highPingComponents = 0UL; + const float highPingInterval = 0.1f; + /// Ping message interval, used to calculate latency / RTT and predicted time. - // 2s was enough to get a good average RTT. - // for prediction, we want to react to latency changes more rapidly. - const float DefaultPingInterval = 0.1f; // for resets - public static float PingInterval = DefaultPingInterval; + // 2s is enough to get a good average RTT. + // For snapshot interpolation and prediction, we need to react to latency changes more rapidly. + internal static float defaultPingInterval = 2.0f; // internal for tests + public static float PingInterval => highPingComponents > 0UL ? highPingInterval : defaultPingInterval; /// Average out the last few results from Ping // const because it's used immediately in _rtt constructor. @@ -129,7 +135,8 @@ public static double predictedTime [RuntimeInitializeOnLoadMethod] public static void ResetStatics() { - PingInterval = DefaultPingInterval; + defaultPingInterval = 2.0f; + highPingComponents = 0UL; lastPingTime = 0; _rtt = new ExponentialMovingAverage(PingWindowSize); #if !UNITY_2020_3_OR_NEWER @@ -147,6 +154,8 @@ internal static void UpdateClient() // Separate method so we can call it from NetworkClient directly. internal static void SendPing() { + //Debug.Log("NetworkTime SendPing"); + // send raw predicted time without the offset applied yet. // we then apply the offset to it after. NetworkPingMessage pingMessage = new NetworkPingMessage diff --git a/Assets/Mirror/Tests/Editor/NetworkConnection/NetworkConnectionToClientTests.cs b/Assets/Mirror/Tests/Editor/NetworkConnection/NetworkConnectionToClientTests.cs index d20edec5e5b..9507e4427db 100644 --- a/Assets/Mirror/Tests/Editor/NetworkConnection/NetworkConnectionToClientTests.cs +++ b/Assets/Mirror/Tests/Editor/NetworkConnection/NetworkConnectionToClientTests.cs @@ -140,7 +140,7 @@ public void UpdatePing_SendsPingWhenIntervalElapsed() float savedPingInterval = NetworkTime.PingInterval; try { - NetworkTime.PingInterval = -1f; + NetworkTime.defaultPingInterval = -1f; NetworkConnectionToClient connection = new NetworkConnectionToClient(1); // Update() calls UpdatePing (fires ping) then flushes the unreliable batcher @@ -152,7 +152,7 @@ public void UpdatePing_SendsPingWhenIntervalElapsed() } finally { - NetworkTime.PingInterval = savedPingInterval; + NetworkTime.defaultPingInterval = savedPingInterval; } } @@ -277,7 +277,7 @@ public void UpdateTimeInterpolation_StepsWhenSnapshotsExist() // StepInterpolation. In edit mode unscaledDeltaTime is typically 0, // so remoteTimeline stays constant, but the branch is fully covered. NetworkConnectionToClient connection = new NetworkConnectionToClient(1); - NetworkTime.PingInterval = float.MaxValue; + NetworkTime.defaultPingInterval = float.MaxValue; connection.OnTimeSnapshot(new TimeSnapshot(NetworkTime.localTime, NetworkTime.localTime)); double timescaleAfterSnapshot = connection.remoteTimescale; @@ -308,7 +308,7 @@ public void Disconnect_SetsIsReadyFalse() public void Cleanup_PreventsQueuedMessageFromBeingSent() { NetworkConnectionToClient connection = new NetworkConnectionToClient(1); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test byte[] message = {0x01, 0x02}; connection.Send(new ArraySegment(message)); @@ -330,7 +330,7 @@ public void Cleanup_PreventsQueuedMessageFromBeingSent() public void Send_BatchesUntilUpdate() { NetworkConnectionToClient connection = new NetworkConnectionToClient(42); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test byte[] message = {0x01, 0x02}; connection.Send(new ArraySegment(message)); @@ -359,7 +359,7 @@ public void SendBatchingResetsPreviousWriter() const int BatchHeader = 8; NetworkConnectionToClient connection = new NetworkConnectionToClient(42); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test // send and update big message byte[] message = {0x01, 0x02}; @@ -390,7 +390,7 @@ public void SendBatchingResetsPreviousWriter() public void Send_UnreliableChannel_BatchesUntilUpdate() { NetworkConnectionToClient connection = new NetworkConnectionToClient(1); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test byte[] message = {0xAA}; connection.Send(new ArraySegment(message), Channels.Unreliable); @@ -411,7 +411,7 @@ public void Send_MultipleMessages_AreBatchedIntoSingleTransportCall() // 1400-byte MemoryTransport threshold, so Batcher packs them into // one batch and Update() makes exactly one SendToTransport call. NetworkConnectionToClient connection = new NetworkConnectionToClient(1); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test connection.Send(new ArraySegment(new byte[]{0x01})); connection.Send(new ArraySegment(new byte[]{0x02})); diff --git a/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs index 3587003a640..0fcca35a9e0 100644 --- a/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs +++ b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs @@ -87,8 +87,7 @@ NetworkIdentity SpawnPrefab(GameObject prefab) [UnityTest] public IEnumerator DisconnectTimeoutTest() { - // Set low ping frequency so no NetworkPingMessage is generated - NetworkTime.PingInterval = 5f; + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test // Set a short timeout for this test and enable disconnectInactiveConnections NetworkServer.disconnectInactiveConnections = true;