From a9188abf603cabf7240c5390bf4f2342331323b0 Mon Sep 17 00:00:00 2001 From: realpoke Date: Fri, 4 Sep 2026 16:46:10 +0200 Subject: [PATCH] fix: Keep rollback input labels collision-free across input delay changes Input labels are computed as tick + input_delay. When input_delay changes at runtime, consecutive ticks can produce duplicate labels (delay decreased) or skip labels entirely (delay increased). Local history and remote receivers then disagree about which input belongs to which tick, corrupting rollback for the affected range. Track the last submitted label: skip labels that would collide, and backfill skipped labels with the current input so the stream stays gap-free. --- addons/netfox/rollback/network-rollback.gd | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/addons/netfox/rollback/network-rollback.gd b/addons/netfox/rollback/network-rollback.gd index a97c2908..318bc7ef 100644 --- a/addons/netfox/rollback/network-rollback.gd +++ b/addons/netfox/rollback/network-rollback.gd @@ -154,6 +154,7 @@ signal after_loop() var _history_limit: int = ProjectSettings.get_setting(&"netfox/rollback/history_limit", 64) var _display_offset: int = ProjectSettings.get_setting(&"netfox/rollback/display_offset", 0) var _input_delay: int = ProjectSettings.get_setting(&"netfox/rollback/input_delay", 0) +var _last_input_label: int = -1 var _input_redundancy: int = ProjectSettings.get_setting(&"netfox/rollback/input_redundancy", 3) # Timing @@ -442,8 +443,25 @@ func _rollback() -> void: _is_rollback = false func _after_tick(tick: int) -> void: - NetworkHistoryServer._record_rollback_input(tick + input_delay) - NetworkSynchronizationServer._synchronize_input(tick + input_delay) + var label: int = tick + input_delay + if label <= _last_input_label: + # Delay decreased: skip this label so the input stream stays + # collision-free (the intent is dropped, matching what receivers keep). + return + + if label > _last_input_label + 1 and _last_input_label >= 0: + # Delay increased: fill the skipped labels with the current input so + # receivers never see a hole in the stream. + var fill_label: int = _last_input_label + 1 + while fill_label <= label: + NetworkHistoryServer._record_rollback_input(fill_label) + NetworkSynchronizationServer._synchronize_input(fill_label) + fill_label += 1 + else: + NetworkHistoryServer._record_rollback_input(label) + NetworkSynchronizationServer._synchronize_input(label) + + _last_input_label = label func _handle_input(snapshot: _Snapshot): if snapshot.is_empty():