Repository navigation
Client LocalTime RPC stamp lags behind server tick at execution #4066
Description
Activity
- addedtype:bugBug ReportBug Reportstat:awaiting-triageStatus - Awaiting triage from the Netcode team.Status - Awaiting triage from the Netcode team.stat:reply-neededAwaiting reply from Unity accountAwaiting reply from Unity account
on Jul 2, 2026 Hi @ertugrulsngr ,
There are a few things here that could be potentially misleading.
- NetworkManager.ServerTime:
- This is really the "network time" that is synchronized to clients. This time should be the most accurate/synchronized time between server/session owner and the rest of the connected clients. You can actually key motion from this time (i.e. a sinusoidal motion) that will look synchronized on all connected clients.
- For comparing half or full roundtrip times, you want to use this value.
- NetworkManager.LocalTime:
- Runs slightly ahead of server time, but can
Take the below screenshots into consideration:
_When comparing times from console output, you want to find the transition point where the server's tick increments and then line up the clients' console log output to match their `ServerTime.Tick` increment. Once you have these, you can see why LocalTime would not be a good choice to determine latency._
- The Host (left side):
- This shows local and server times being the same (as it should).
- The Clients (right side top and bottom):
- This shows each client's local times have a larger delta between the two where one client is still on tick 524 (about 1-2 ticks ahead of server time) while another is on tick 525 (about 2-3 ticks ahead of server).
- The client's ServerTimes are a closer match to each other while their LocalTimes are not so much.
Which to use?
- If you were to send the LocalTime values, then there would be larger deltas.
- If you were to send the ServerTime values, then there would be smaller deltas.
I would recommend using ServerTime.
NGO docs suggest LocalTime predicts the server tick when an action is processed. We expected:
Could you send me the link to the section of our documentation that describes this?
I think the context is going to be when the Server is sending to the ClientIf the server were to send an RPC to a client using its Server/Local time, the client's local time would be more accurate than if you send a client's local time to the server since the client's local time can be between 2 to 3+ ticks ahead (increases as latency increases)... which is the behavior you are describing.
When comparing time between clients and server, you need to first think about the context... are you trying to determine the latency between a server and client or a client to a client? Most of the time, using ServerTime will yield a more accurate delta than using LocalTime.
Server time is synchronized by the server (or for distributed authority it is the service that provides the ServerTime) on a regular basis.
Local time runs ahead of the server time relative to continually running calculations based off of a client's latency to the server and the frequently updated ServerTime.Using the below script:
[Rpc(SendTo.NotMe)] private void ServerPingsClientsRpc(float serverTime, int tick) { var serverTimeDelta = NetworkManager.ServerTime.TimeAsFloat - serverTime; var serverTickDelta = NetworkManager.ServerTime.Tick - tick; var localTimeDelta = NetworkManager.LocalTime.TimeAsFloat - serverTime; var localTickDelta = NetworkManager.LocalTime.Tick - tick; Debug.Log($"[Server to Clients][ServerTime] Client-{NetworkManager.LocalClientId} has a {serverTimeDelta * 1000}ms latency and {serverTickDelta} delta from the server "); Debug.Log($"[Server to Clients][LocalTime] Client-{NetworkManager.LocalClientId} has a {localTimeDelta * 1000}ms latency and {localTickDelta} delta from the server "); }
Using the above RPC, I ran a session where the host sends the client an RPC containing its ServerTime time as float and its current tick.
You can see the client's server time lags behind the server by roughly 49ms while the client's local time runs about 54ms ahead. The respective deltas show the client's server time tick value is about one tick behind while the client's local time is about one tick ahead.
Let me know if this information helps clarify the differences between the two?
If you still think there is an issue or have more questions please feel free to post here.- NetworkManager.ServerTime:
- addedstat:awaiting-responseAwaiting response from author. This label should be added manually.Awaiting response from author. This label should be added manually.and removedstat:reply-neededAwaiting reply from Unity accountAwaiting reply from Unity account
on Jul 7, 2026 - addedstat:InvestigatingIssue is currently being investigatedIssue is currently being investigated
on Jul 7, 2026 Hi @NoelStephensUnity ,
Thanks for the detailed explanation! Looking back at my initial post, I realize my explanation was a bit superficial, so I apologize for that. Let me provide a more precise breakdown of the issue and what I am trying to achieve.
To answer your question regarding the documentation, the behavior I expected is directly based on the official Unity Netcode for GameObjects documentation at Netcode Time and Ticks:
which explicitly states:
"LocalTime on a client is ahead of the server. If a server RPC is sent at LocalTime from a client it will roughly arrive at ServerTime on the server."
Based on this exact quote, I expected that the server tick on the RPC handler would be roughly equal to the sent
LocalTimetick. However, as latency increases, my logs show the opposite behavior: the sentLocalTimetick lags behind the server's execution tick, indicating that the current documentation might be incorrect or misleading.To understand the underlying mechanics, I did a deep dive into the NGO source code to see how time synchronization is handled inside
TimeSyncMessage.cs:public void Handle(ref NetworkContext context) { var networkManager = (NetworkManager)context.SystemOwner; var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, Tick); networkManager.NetworkTimeSystem.SyncCount++; networkManager.NetworkTimeSystem.Sync( time.Time, networkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(context.SenderId) / 1000d ); }
When a time sync message arrives, these values are set immediately, and the system continues to extrapolate the time on every update. However, during this sync call, the system uses the raw server tick without offsetting it by the packet's one-way travel latency (such as adding half of the RTT to capture the actual server time at the exact moment of packet arrival).
Because of this design choice,
ServerTimeon the client inherently lags behind the actual server. Since NGO does not expose a public event or hook when this sync message is handled, it is currently impossible for me to intercept it and construct a more precise, latency-compensated timeline manually.To give you some context on why I need this precision, I am developing a client-side prediction movement system. The client needs to predict the future and stamp its inputs with the exact tick they are expected to execute upon arriving at the server.
I would highly appreciate your professional feedback or advice on whether this input stamping methodology is the correct approach in NGO, or if there is a better practice you recommend from your experience.
As a workaround, I experimented with manually offsetting
ServerTimeby adding the current full RTT in ticks to it. In practice, this empirical approach works successfully and gives us the desired behavior. However, it feels theoretically incorrect.Because sync messages arrive roughly once per second, the transport RTT can fluctuate between sync points. Attempting to compensate by adding a fluctuating RTT to an already shifted, extrapolated timeline, without knowing the exact baseline RTT at the moment the sync packet originally arrived, lacks a solid theoretical foundation.
I would highly appreciate your architectural insights on how I should handle input prediction stamping and time precision in NGO given these constraints.
- addedstat:reply-neededAwaiting reply from Unity accountAwaiting reply from Unity accountand removedstat:awaiting-responseAwaiting response from author. This label should be added manually.Awaiting response from author. This label should be added manually.
on Jul 8, 2026 Hey @ertugrulsngr,
Thank you for further clarifying what you are wanting to accomplish along with the additional details.
🥇To give you some context on why I need this precision, I am developing a client-side prediction movement system. The client needs to predict the future and stamp its inputs with the exact tick they are expected to execute upon arriving at the server.
When you say "inputs" are these player movement and/or interaction type inputs?
If so, then it sounds like you want to have server-side input reconciliation where (typically) the reconciliation period is several ticks (at least more than one) and the server timestamps the inputs based on the local server time (when received while also taking the client's latency into consideration). When the reconciliation time is up. the server will:- Order the received inputs from clients (if needed) based on the time received (the timestamp already subtracts the average client's latency).
- Processes the queue it has accumulated.
- Begin the next reconciliation period.
Where the reconciliation period is at least 2 network ticks or more.
So you really end up with a network tick and reconciliation tick where the later isn't typically synchronized with clients.Now, if you want true prediction where the client goes ahead and begins to act upon the action before the server has reconciled then you would want the ability to rollback on the client side. Do you plan on having a form of rollback on the client side?
- addedstat:awaiting-responseAwaiting response from author. This label should be added manually.Awaiting response from author. This label should be added manually.and removedstat:reply-neededAwaiting reply from Unity accountAwaiting reply from Unity account
on Jul 15, 2026 This issue has been automatically marked as stale because it has been awaiting response for over 2 weeks without any activity.
Please update the issue with any new information or it may be closed in 2 weeks.- addedStaleAdded after 30 days since stat:awaiting response was added (if it's still present)Added after 30 days since stat:awaiting response was added (if it's still present)
on Jul 30, 2026 This issue has been automatically closed because it has been stale for 2 weeks without any activity. Feel free to reopen if you have new information to add.
- removedstat:awaiting-responseAwaiting response from author. This label should be added manually.Awaiting response from author. This label should be added manually.stat:awaiting-triageStatus - Awaiting triage from the Netcode team.Status - Awaiting triage from the Netcode team.stat:InvestigatingIssue is currently being investigatedIssue is currently being investigatedStaleAdded after 30 days since stat:awaiting response was added (if it's still present)Added after 30 days since stat:awaiting response was added (if it's still present)
on Aug 14, 2026
Description
When a client sends an RPC stamped with
LocalTime.Tick, the serverServerTime.Tickin the RPC handler is greater than the sent value. The gap grows with Network Simulator packet delay.NGO docs suggest
LocalTimepredicts the server tick when an action is processed. We expected:server_tick_on_handler <= sent_LocalTime.TickAfter steady-state (30s+), we see the opposite.
GetCurrentRtt()reports correctly (~2× packet delay), so this does not look like an RTT reporting or slow-adaptation issue.Reproduce Steps
NetworkBehaviourthat on each clientNetworkTickSystem.TicksendsLocalTime.Tickvia ServerRpc; server logs received tick + currentServerTime.TickActual Outcome
Gap scales with delay.
ServerTime.Tick + RTT_ticksat send is ~1 tick off;LocalTime.Tickis ~3–8 ticks off.Expected Outcome
server_tick_on_rpc_handler <= sent_LocalTime.Tick(ideally ≈ equal, ±1 tick). Gap should not grow linearly with delay at steady-state.Environment