From 5ef7bf08e10c97e595c321316d3bd7c0e49bcfec Mon Sep 17 00:00:00 2001 From: Danil Tkach Date: Sat, 17 Oct 2026 21:45:54 +0300 Subject: [PATCH] Don't send keepalive until the client acknowledges FinishedUpdate The keepalive task starts right after the CONFIG -> PLAY switch packets are written, with a 250 ms initial delay. If the client is still applying the registries when it fires, a PLAY keepalive reaches it between sending its FinishConfiguration acknowledgement and installing the PLAY outbound protocol; the client answers it immediately and disconnects with "Pipeline has no outbound protocol configured, can't process packet ServerboundKeepAlivePacket". Vanilla servers send nothing in this window, so skip keepalive ticks until the play transition completes. --- .../elytrium/limboapi/server/LimboSessionHandlerImpl.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin/src/main/java/net/elytrium/limboapi/server/LimboSessionHandlerImpl.java b/plugin/src/main/java/net/elytrium/limboapi/server/LimboSessionHandlerImpl.java index 61e76037..58070740 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/server/LimboSessionHandlerImpl.java +++ b/plugin/src/main/java/net/elytrium/limboapi/server/LimboSessionHandlerImpl.java @@ -141,6 +141,13 @@ public void onConfig(LimboPlayer player) { return; } + // FinishedUpdate is already sent, but the client hasn't acknowledged it yet. Vanilla clients answer + // a keepalive received in this window while their outbound protocol is unconfigured and disconnect with + // "Pipeline has no outbound protocol configured", so wait for the acknowledgement like vanilla servers do. + if (!this.playTransition.isDone()) { + return; + } + if (this.keepAlivePending) { if (++this.keepAlivesSkipped == 2) { connection.closeWith(this.plugin.getPackets().getTimeOut(this.player.getConnection().getState()));