Skip to content

reset pending keepalive state on listener handoff during reconfiguration#13712

Open
ZECHEESELORD wants to merge 2 commits intoPaperMC:mainfrom
ZECHEESELORD:fix/keepalive-listener-handoff
Open

reset pending keepalive state on listener handoff during reconfiguration#13712
ZECHEESELORD wants to merge 2 commits intoPaperMC:mainfrom
ZECHEESELORD:fix/keepalive-listener-handoff

Conversation

@ZECHEESELORD
Copy link

Fixes PaperMC/Velocity#1723. (The backend side)

During mid session reconfiguration behind Velocity, Paper was carrying the same KeepAlive tracker across listener handoff. That allowed a pending PLAY keepalive expectation to survive into the CONFIG listener.

In the failing case, the sequence was:

  1. the PLAY listener issued keepalive A
  2. listener handoff moved the player into CONFIG
  3. the CONFIG listener inherited the pending expectation for A
  4. the CONFIG listener issued keepalive B
  5. the reply for B arrived while the backend still considered A outstanding
  6. Paper rejected B as stale or out-of-order and disconnected the player with disconnect.timeout

The key signal from backend logging was that listener_handoff showed the CONFIG listener inheriting the prior PLAY expectedId, and the next CONFIG keepalive reply was then rejected as stale_or_out_of_order.

So the failure was not that Velocity dropped the CONFIG reply (the backend received it). The problem was that stale pending PLAY keepalive state leaked across the PLAY -> CONFIG handoff.

the fix:

ServerCommonPacketListenerImpl#createCookie(...) now gives the next listener a fresh KeepAlive instance instead of reusing the current listener's pending tracker. This preserves latency/history state, but intentionally resets inflight keepalive challenge state across listener handoff. In other words:

  • preserve ping history
  • reset pending keepalive expectations

That restores the expected protocol boundary between PLAY and CONFIG.

testing:

Tested locally with the Velocity reproduction from PaperMC/Velocity#1723.

Before this fix:

  • repeated config/unconfig cycles would eventually fail
  • failures were much easier to trigger with added latency
  • backend logs showed CONFIG replies being rejected as out-of-order because the old PLAY expectation was still present

After this fix:

  • no disconnects at 0 ms added latency
  • no disconnects at 200 ms added latency
  • no disconnects at 800 ms added latency
  • backend logs show the CONFIG listener no longer inherits the stale PLAY pending keepalive expectation

@ZECHEESELORD ZECHEESELORD marked this pull request as ready for review March 21, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Awaiting review

Development

Successfully merging this pull request may close these issues.

Velocity does not support re-entering the configuration phase from play.

1 participant