From 71b89d63754dc57fd883760c9b9d862a10fdb223 Mon Sep 17 00:00:00 2001
From: MrGadget <9826063+MrGadget1024@users.noreply.github.com>
Date: Fri, 27 Feb 2026 13:21:49 -0500
Subject: [PATCH 1/2] feat: Quiet Mode for certain game types Network Manager
now has a checkbox to enable Quiet Mode - Disables TimeSnapshotMessage
entirely - Forces PingMessage frequency to 0.5Hz (2 sec interval) - Only for
games NOT using NT or PredictedRB (warnings included) - Unit tests updated
accordingly
---
.../NetworkTransform/NetworkTransformBase.cs | 3 +++
.../PredictedRigidbody/PredictedRigidbody.cs | 6 ++++++
Assets/Mirror/Core/NetworkClient.cs | 7 ++++---
Assets/Mirror/Core/NetworkManager.cs | 5 +++++
Assets/Mirror/Core/NetworkServer.cs | 5 +++--
Assets/Mirror/Core/NetworkTime.cs | 16 ++++++++++-----
.../NetworkConnectionToClientTests.cs | 20 +++++++++++--------
.../PrefabWithChildrenForClientScene.prefab | 6 ++++--
.../ValidPrefabForClientScene.prefab | 3 ++-
.../Tests/Runtime/NetworkServerRuntimeTest.cs | 2 +-
10 files changed, 51 insertions(+), 22 deletions(-)
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/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab
index 91e682b13eb..9ee1703f642 100644
--- a/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab
+++ b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab
@@ -24,13 +24,14 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3117426950187087154}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6537489145038351880}
m_Father: {fileID: 0}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5409067437793000088
MonoBehaviour:
@@ -73,12 +74,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3264653828050749140}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 51440471024079650}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6548650892574975460
MonoBehaviour:
diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab
index f881c276a9c..9e15c87400e 100644
--- a/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab
+++ b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab
@@ -24,12 +24,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9072865897540379920}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1423274632536207763
MonoBehaviour:
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;
From 5f83156b33ad3064443224e7e3ea122c19a2ef15 Mon Sep 17 00:00:00 2001
From: MrGadget <9826063+MrGadget1024@users.noreply.github.com>
Date: Fri, 27 Feb 2026 13:32:32 -0500
Subject: [PATCH 2/2] revert prefab changes
---
.../TestPrefabs/PrefabWithChildrenForClientScene.prefab | 6 ++----
.../Editor/TestPrefabs/ValidPrefabForClientScene.prefab | 3 +--
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab
index 9ee1703f642..91e682b13eb 100644
--- a/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab
+++ b/Assets/Mirror/Tests/Editor/TestPrefabs/PrefabWithChildrenForClientScene.prefab
@@ -24,14 +24,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3117426950187087154}
- serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6537489145038351880}
m_Father: {fileID: 0}
+ m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5409067437793000088
MonoBehaviour:
@@ -74,13 +73,12 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3264653828050749140}
- serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 51440471024079650}
+ m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6548650892574975460
MonoBehaviour:
diff --git a/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab
index 9e15c87400e..f881c276a9c 100644
--- a/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab
+++ b/Assets/Mirror/Tests/Editor/TestPrefabs/ValidPrefabForClientScene.prefab
@@ -24,13 +24,12 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9072865897540379920}
- serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
+ m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1423274632536207763
MonoBehaviour: