diff --git a/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs b/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs index 9d948a4b520..50b7af935ce 100644 --- a/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs +++ b/Assets/Mirror/Components/NetworkTransform/NetworkTransformBase.cs @@ -457,6 +457,9 @@ protected virtual void OnEnable() { ResetState(); + if (NetworkTime.quietMode) + Debug.LogWarning("NetworkTransform: Quiet Mode is enabled, which will cause issues with interpolation. Please disable Quiet Mode in Network Manager."); + if (NetworkServer.active) NetworkIdentity.clientAuthorityCallback += OnClientAuthorityChanged; } diff --git a/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs b/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs index 51188478068..65876752bef 100644 --- a/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs +++ b/Assets/Mirror/Components/PredictedRigidbody/PredictedRigidbody.cs @@ -145,6 +145,12 @@ protected virtual void Awake() positionCorrectionThresholdSqr = positionCorrectionThreshold * positionCorrectionThreshold; } + protected virtual void OnEnable() + { + if (NetworkTime.quietMode) + Debug.LogWarning("PredictedRigidbody: Quiet Mode is enabled, which will cause issues with prediction. Please disable Quiet Mode in Network Manager."); + } + 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..5f646b58a9c 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Mirror.RemoteCalls; @@ -1792,8 +1792,9 @@ static void Broadcast(bool unreliableBaselineElapsed) // nothing to do in host mode. server already knows the state. if (NetworkServer.active) return; - // send time snapshot every sendInterval. - Send(new TimeSnapshotMessage(), Channels.Unreliable); + // send time snapshot every sendInterval unless in quiet mode + if (!NetworkTime.quietMode) + Send(new TimeSnapshotMessage(), Channels.Unreliable); // broadcast client state to server BroadcastToServer(unreliableBaselineElapsed); diff --git a/Assets/Mirror/Core/NetworkManager.cs b/Assets/Mirror/Core/NetworkManager.cs index 80c588bab5c..2a7e81a3b12 100644 --- a/Assets/Mirror/Core/NetworkManager.cs +++ b/Assets/Mirror/Core/NetworkManager.cs @@ -38,6 +38,9 @@ public class NetworkManager : MonoBehaviour public bool editorAutoStart; [Header("Sync Settings")] + [Tooltip("Enable Quiet Mode when not using any NetworkTransform or PredictedRigidBody components.\nThis CANNOT be changed at runtime after server starts!")] + public bool quietMode = false; + /// Server Update frequency, per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE. [Tooltip("Server / Client send rate per second.\nUse 60-100Hz for fast paced games like Counter-Strike to minimize latency.\nUse around 30Hz for games like WoW to minimize computations.\nUse around 1-10Hz for slow paced games like EVE.")] [FormerlySerializedAs("serverTickRate")] @@ -319,6 +322,8 @@ void SetupServer() ConfigureHeadlessFrameRate(); + NetworkTime.quietMode = quietMode; + // start listening to network connections NetworkServer.Listen(maxConnections); diff --git a/Assets/Mirror/Core/NetworkServer.cs b/Assets/Mirror/Core/NetworkServer.cs index a7640dfbe75..ed4453ed971 100644 --- a/Assets/Mirror/Core/NetworkServer.cs +++ b/Assets/Mirror/Core/NetworkServer.cs @@ -2272,7 +2272,7 @@ static void Broadcast(bool unreliableBaselineElapsed) // pull in UpdateVarsMessage for each entity it observes if (connection.isReady) { - // send time for snapshot interpolation every sendInterval. + // send time for snapshot interpolation every sendInterval unless quietMode is enabled. // BroadcastToConnection() may not send if nothing is new. // // sent over unreliable. @@ -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.quietMode) + 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..a7c1cb78ea1 100644 --- a/Assets/Mirror/Core/NetworkTime.cs +++ b/Assets/Mirror/Core/NetworkTime.cs @@ -16,11 +16,16 @@ namespace Mirror /// Synchronizes server time to clients. public static class NetworkTime { + // Set true from NetworkManager to disable TimeSnapshotMessages and limit PingMessage to 0.5Hz + internal static bool quietMode; + + 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 => quietMode ? defaultPingInterval: highPingInterval; /// Average out the last few results from Ping // const because it's used immediately in _rtt constructor. @@ -129,7 +134,8 @@ public static double predictedTime [RuntimeInitializeOnLoadMethod] public static void ResetStatics() { - PingInterval = DefaultPingInterval; + defaultPingInterval = 2.0f; + quietMode = false; lastPingTime = 0; _rtt = new ExponentialMovingAverage(PingWindowSize); #if !UNITY_2020_3_OR_NEWER diff --git a/Assets/Mirror/Tests/Editor/NetworkConnection/NetworkConnectionToClientTests.cs b/Assets/Mirror/Tests/Editor/NetworkConnection/NetworkConnectionToClientTests.cs index d20edec5e5b..37bde2b98c3 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,8 @@ public void Disconnect_SetsIsReadyFalse() public void Cleanup_PreventsQueuedMessageFromBeingSent() { NetworkConnectionToClient connection = new NetworkConnectionToClient(1); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.quietMode = true; // avoid log spam about connectionId mismatch since we're not calling Connect() on this instance + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test byte[] message = {0x01, 0x02}; connection.Send(new ArraySegment(message)); @@ -330,7 +331,8 @@ public void Cleanup_PreventsQueuedMessageFromBeingSent() public void Send_BatchesUntilUpdate() { NetworkConnectionToClient connection = new NetworkConnectionToClient(42); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.quietMode = true; // avoid log spam about connectionId mismatch + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test byte[] message = {0x01, 0x02}; connection.Send(new ArraySegment(message)); @@ -359,7 +361,8 @@ public void SendBatchingResetsPreviousWriter() const int BatchHeader = 8; NetworkConnectionToClient connection = new NetworkConnectionToClient(42); - NetworkTime.PingInterval = float.MaxValue; // disable ping for this test + NetworkTime.quietMode = true; // avoid log spam about connectionId mismatch + NetworkTime.defaultPingInterval = float.MaxValue; // disable ping for this test // send and update big message byte[] message = {0x01, 0x02}; @@ -390,7 +393,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 +414,8 @@ 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.quietMode = true; // avoid log spam about connectionId mismatch + 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..e02444d7653 100644 --- a/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs +++ b/Assets/Mirror/Tests/Runtime/NetworkServerRuntimeTest.cs @@ -88,7 +88,7 @@ NetworkIdentity SpawnPrefab(GameObject prefab) public IEnumerator DisconnectTimeoutTest() { // Set low ping frequency so no NetworkPingMessage is generated - NetworkTime.PingInterval = 5f; + NetworkTime.defaultPingInterval = float.MaxValue; // Set a short timeout for this test and enable disconnectInactiveConnections NetworkServer.disconnectInactiveConnections = true;