Fix Seamoth movement sync timing#2753
Conversation
I tested restoring TimeManager.RealTimeElapsed for both MovementBroadcaster and MovementReplicator, but that regressed the Seamoth movement fix completely: neither client could reliably see the other client's Seamoth movement again. As I expected, this is part of the fix for this movement path. The original bug appears tied to using the synchronized/game-time path for movement interpolation timing, especially when ownership changes and remote snapshots are buffered. I agree that time-of-day changes are important and should not introduce vehicle lag. However, reverting the movement clock back to TimeManager reintroduces the core desync. I am going to investigate whether the TimeManager/time-of-day issue needs a separate mitigation, but the Seamoth sync fix currently requires the movement broadcaster/replicator to use Unity realtime for local interpolation scheduling. I will say this, I don't see you r average user jumping through the time of day with admin commands and immediately jumping into the seamoth while having another player watching them, but I could be wrong lol. I am still going to try to make that not happen of course, but changing these back does not appear to be possible while making the Seamoth still sync properly with the way things are currently written. |
|
Update: I also specifically tested the time-jump edge case mentioned above. Using the current version of this PR, I rapidly cycled time through day/night roughly 10 times, then immediately entered and drove the Seamoth. I did not see any lag, delayed interpolation, catch-up movement, or “makeup” movement afterward. Seamoth movement continued to sync normally and behaved as expected. I repeated this several times, with both players. So based on this test, using |
|
I also wanted to add that I test with 200ms, 400ms, and 200ms to 800ms jitter with LiteNetLib and it worked better there was no issues at all. Interpolation actually made the jitter that was noticeable on the character, completely unnoticeable. |
Measurity
left a comment
There was a problem hiding this comment.
Thanks! Stutter as time goes on is gone now, I must have mixed up a test before.
Summary
Fixes a Seamoth / remote vehicle movement desync where Seamoth movement could fail to appear on other clients, appear only intermittently, or replay later as delayed/catch-up movement.
During testing, the original behavior was broader than a simple one-way sync issue:
The final fix resolves both the broader Seamoth movement visibility failure and the later narrowed login-order-dependent failure.
Related issue:
Root cause
The vehicle movement path was mixing time bases and retaining stale interpolation state across ownership transitions.
Debugging showed that the issue was not primarily caused by server-side ownership denial:
The remaining failure was in client-side movement timing and interpolation.
The vehicle broadcaster and replicator were using Nitrox TimeManager.RealTimeElapsed, while received vehicle snapshots were being scheduled in a way that could leave the observer client's interpolation buffer out of phase with the sender's movement stream.
As testing progressed, this created several visible failure modes:
Changes
1. Use Unity realtime for local vehicle movement broadcast cadence
MovementBroadcaster.Update() now uses Time.realtimeSinceStartup instead of TimeManager.RealTimeElapsed.
This keeps the local vehicle broadcast cadence tied to Unity realtime.
2. Use Unity realtime for movement replication
MovementReplicator.CurrentTime now uses Time.realtimeSinceStartup instead of TimeManager.RealTimeElapsed.
This keeps the receiver-side interpolation path on the same local Unity realtime basis as the broadcaster.
3. Schedule received snapshots relative to receiver time
Received snapshots are now scheduled with:
float occurrenceTime = currentTime + INTERPOLATION_TIME;instead of scheduling from the packet timestamp plus measured latency.
This avoids treating another client's movement timestamp as directly comparable to the receiving client's local interpolation time.
4. Trim stale interpolation backlog
If the movement buffer grows beyond one broadcast-frequency window, the replicator drops stale snapshots and keeps only the latest small interpolation window.
This prevents remote vehicles from replaying a long backlog of stale movement after the receiver falls behind.
5. Clear stale vehicle buffer on remote ownership transition
When a vehicle becomes remote-owned on a client and the client already has a MovementReplicator, the existing interpolation buffer is cleared.
This prevents snapshots from the previous ownership phase from being mixed with the new driver's movement stream.
Testing
Tested with two clients and one Seamoth.
Before the final fix
Initial testing showed the Seamoth movement path was broadly unreliable:
After initial debugging and partial timing changes, the behavior narrowed into a deterministic login-order-dependent failure:
This confirmed the issue was not tied to a specific player account and was not random. It was related to client timing, interpolation state, and ownership transition handling.
Debug confirmation
Additional debug logging confirmed:
After the final fix
Tested:
Result:
Why this is necessary
Seamoths are central to normal Subnautica progression and multiplayer exploration. When this bug occurs, one or more clients can see a driver move locally while other clients see no correct vehicle movement, delayed movement, or old-position replay.
That causes practical multiplayer desync:
This PR fixes the client-side timing/interpolation path that caused those failures.
Notes
This PR intentionally does not change server-side simulation ownership rules.
Server-side debugging showed ownership transfer was working. The fix is limited to client-side vehicle movement timing, interpolation backlog handling, and stale receiver-state cleanup across ownership changes.