Skip to content

fix: Recover clock from pauses and stalls#381

Merged
elementbound merged 5 commits intofoxssake:mainfrom
Riordan-DC:handle-pause-fix
Jan 14, 2025
Merged

fix: Recover clock from pauses and stalls#381
elementbound merged 5 commits intofoxssake:mainfrom
Riordan-DC:handle-pause-fix

Conversation

@Riordan-DC
Copy link
Copy Markdown
Contributor

ISSUE DESCRIPTION

This is a fix for clients remaining desynchronized from the server when freezing / pausing their games.

When freezing or pausing the game the network-time.gd handles this pause when it detects a large time has elapsed since the last update.

EXPLAINATION

When pausing or freezing the network timers continue to update so our network clock stays synced and our reference clock requires little catchup. However, our _tick variable won't be automatically updated (this is what actually drives the multiplayer).

CONSEQUENCES OF ISSUE

The result is a client who thinks they are in the past, this has two consequences:
The server will interpret the clients inputs as old (older than the max allowed history) and ignore them. The player will then be frozen.
The client will interpret server updates as being in the future and wont run them until its _tick variable has "caught up". The effect is that a client sees server updates with a delay equal to the time frozen/paused.

This does not just resolve itself and currently breaks the client until they disconnect/reconnect.

FIX DESCRIPTION

This commit adds a line which updates the _tick to the current network time after a pause is detected. This PR doesn't clear the rollback buffers and will mean that potentially some client inputs will still be sent with an old _tick (not a big deal as new inputs will now have the updated _tick but it would be nice to add).

I first noticed this issue when I paused by games and noticed my client never "resyncs" with the server, in actuality its clock was synced but its _ticks was not. To reproduce / test this issue our yourself add this code to your game.
I tested this PR by adding it to the player.gd script in the multiplayer-fps example.

func _process(delta: float) -> void:
	if Input.is_key_pressed(KEY_T):
                # Uncomment below to test freezes
		OS.delay_msec(2000)

                # Uncomment below to test pauses
		#get_tree().create_timer(2.0, true, false, true).timeout.connect(get_tree().set_pause.bind(false))
		#get_tree().set_pause(true)

Then create to instances of the fps game, one as host, and other as client. As the client press T and introduce delay until the timer becomes permanently behind and becomes unresponsive.

@elementbound
Copy link
Copy Markdown
Contributor

Thanks a lot for the research and the PR @Riordan-DC! Am I correct in assuming that this also fixes #332?

The pause detection logic only runs when launched from the editor atm, I think we could remove that condition, and make the pause threshold configurable, instead of being hardcoded at 1 second. I'll add these changes soon!

@Riordan-DC
Copy link
Copy Markdown
Contributor Author

Thanks a lot for the research and the PR @Riordan-DC! Am I correct in assuming that this also fixes #332?

The pause detection logic only runs when launched from the editor atm, I think we could remove that condition, and make the pause threshold configurable, instead of being hardcoded at 1 second. I'll add these changes soon!

yes thats correct it fixes that issue and yes I think that pause/freeze logic would be good even when not in the editor to handle people who minimize or otherwise freeze their game.

@elementbound elementbound changed the title Updates _tick with new time if pause/freeze detected fix: Recover clock from pauses and stalls Jan 14, 2025
@elementbound elementbound merged commit 1c18a15 into foxssake:main Jan 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants