Best way to handle client pausing multiplayer game for a short time

The title is kind of unclear so I’ll explain it better here.

Basically quite a few things in my 2d multiplayer (client hosted with Relay and server authority) game are handled locally on the clients to reduce bandwidth and make things run smoother. For example I spawn many objects that have a constant rotation in the game so I just tell the clients to rotate it themselves this much every frame, but there’s an issue with this solution.

Although I have “run in background” enabled I’m worried about the behavior if a client suspends the game for a short time (for example on Steam Deck if the player suspends the game and them resumes it) and if this will be similar to when I test in the editor pausing and pausing on one client and then continuing to play the game. When I do this all the objects that were being determined locally (such as the rotation of all the objects I spawn in at the start) now their rotation is not in sync with the other clients causing the collisions to look bad on the other clients because they are not lining up with the objects on their game.

Does anyone know a good way to get around this issue? Hopefully I explained this well enough and any help is much appreciated!

One approach I like to use is to change as many timers as possible from using deltatime to using realtimeSinceStartup. For a rotating object, for example, instead of adding some factor times Time.deltaTime to its rotation each frame I’d set its rotation to a factor of Time.realtimeSinceStartup minus the recorded realtimeSinceStartup when it started moving.

Imagine two clients, Client 1 has Time.realtimeSinceStartup of 57.5 seconds and Client 2 started up earlier so it has 122.3 seconds. The server sends an RPC to all clients to tell it to start rotating a platform, client 1 records that it happened at 57.5 seconds on its clock and client 2 records that it happened on 122.3 seconds on its clock. Now in the update/fixedupdate on each client they can just subtract the recorded start time from the current Time.realtimeSinceStartup and should both arrive at the same number of seconds.

Rather than adding to the rotation angle in update/fixedupdate, you set the rotation angle to that number times a speed factor. Now even if the game pauses or loses focus or runs slow/late on its updates/fixed updates it always sets the rotation to the correct state when it resumes. You do need to send a synchronised command to start the action and record the start time or to do it OnNetworkSpawn, but you have to do that anyway.

1 Like

I took me a while to get around to this but I just implemented it and it works great, this was exactly what I was looking for. Thanks!

1 Like

You might also be able to find some useful insights with this example. It uses NetworkManager.NetworkTimeSystem.ServerTime as the reference point in order to assure that the time is synchronized off of the network time.
TimeSyncMotion.zip (96.2 KB)

Another way to synchronize motion based off of time.

1 Like