Merge pull request #101 from Zhyren-git/fix/keepalive-timeout

Fixes player getting "Timed out" while standing still due to Keep-Alive not being sent correctly.
This commit is contained in:
LOOHP 2025-12-18 17:00:58 +00:00 committed by GitHub
commit 835caf7d71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 30 additions and 23 deletions

View File

@ -43,7 +43,6 @@ import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.GlobalPos; import com.loohp.limbo.location.GlobalPos;
import com.loohp.limbo.location.Location; import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.protocol.packets.ClientboundFinishConfigurationPacket; import com.loohp.limbo.network.protocol.packets.ClientboundFinishConfigurationPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundLevelChunkWithLightPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundRegistryDataPacket; import com.loohp.limbo.network.protocol.packets.ClientboundRegistryDataPacket;
import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn; import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn;
import com.loohp.limbo.network.protocol.packets.PacketIn; import com.loohp.limbo.network.protocol.packets.PacketIn;
@ -112,7 +111,6 @@ import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.utils.InventoryClickUtils; import com.loohp.limbo.utils.InventoryClickUtils;
import com.loohp.limbo.utils.MojangAPIUtils; import com.loohp.limbo.utils.MojangAPIUtils;
import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse;
import com.loohp.limbo.world.BlockPosition;
import com.loohp.limbo.world.BlockState; import com.loohp.limbo.world.BlockState;
import com.loohp.limbo.world.World; import com.loohp.limbo.world.World;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
@ -164,10 +162,12 @@ public class ClientConnection extends Thread {
private boolean running; private boolean running;
private volatile ClientState state; private volatile ClientState state;
private final AtomicLong lastPacketTimestamp;
private final AtomicLong lastKeepAlivePayLoad;
private final AtomicLong lastKeepAliveResponse;
private Player player; private Player player;
private TimerTask keepAliveTask; private TimerTask keepAliveTask;
private AtomicLong lastPacketTimestamp;
private AtomicLong lastKeepAlivePayLoad;
private InetAddress inetAddress; private InetAddress inetAddress;
private boolean ready; private boolean ready;
@ -176,6 +176,7 @@ public class ClientConnection extends Thread {
this.inetAddress = clientSocket.getInetAddress(); this.inetAddress = clientSocket.getInetAddress();
this.lastPacketTimestamp = new AtomicLong(-1); this.lastPacketTimestamp = new AtomicLong(-1);
this.lastKeepAlivePayLoad = new AtomicLong(-1); this.lastKeepAlivePayLoad = new AtomicLong(-1);
this.lastKeepAliveResponse = new AtomicLong(-1);
this.channel = null; this.channel = null;
this.running = false; this.running = false;
this.ready = false; this.ready = false;
@ -193,6 +194,10 @@ public class ClientConnection extends Thread {
this.lastKeepAlivePayLoad.set(payLoad); this.lastKeepAlivePayLoad.set(payLoad);
} }
public long getLastKeepAliveResponse() {
return lastKeepAliveResponse.get();
}
public long getLastPacketTimestamp() { public long getLastPacketTimestamp() {
return lastPacketTimestamp.get(); return lastPacketTimestamp.get();
} }
@ -251,6 +256,9 @@ public class ClientConnection extends Thread {
} catch (IOException ignored) { } catch (IOException ignored) {
} }
try { try {
ServerProperties properties = Limbo.getInstance().getServerProperties();
String str = (properties.isLogPlayerIPAddresses() ? inetAddress.getHostName() : "<ip address withheld>") + ":" + clientSocket.getPort();
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player disconnected with the reason " + PlainTextComponentSerializer.plainText().serialize(reason));
clientSocket.close(); clientSocket.close();
} catch (IOException ignored) { } catch (IOException ignored) {
} }
@ -650,22 +658,21 @@ public class ClientConnection extends Thread {
keepAliveTask = new TimerTask() { keepAliveTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
if (state.equals(ClientState.DISCONNECTED)) { if (state != ClientState.PLAY || !ready) {
this.cancel(); this.cancel();
} else if (ready && state.equals(ClientState.PLAY)) { }
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - getLastPacketTimestamp() > 15000) { PacketPlayOutKeepAlive keepAlive = new PacketPlayOutKeepAlive(now);
PacketPlayOutKeepAlive keepAlivePacket = new PacketPlayOutKeepAlive(now);
try { try {
sendPacket(keepAlivePacket); sendPacket(keepAlive);
setLastKeepAlivePayLoad(now); setLastKeepAlivePayLoad(now);
} catch (Exception e) { } catch (IOException e) {
} cancel();
}
} }
} }
}; };
new Timer().schedule(keepAliveTask, 5000, 10000); new Timer().schedule(keepAliveTask, 0, 10000);
while (clientSocket.isConnected()) { while (clientSocket.isConnected()) {
try { try {
@ -717,12 +724,12 @@ public class ClientConnection extends Thread {
processMoveEvent.consume(event, to); processMoveEvent.consume(event, to);
} }
} else if (packetIn instanceof PacketPlayInKeepAlive) { } else if (packetIn instanceof PacketPlayInKeepAlive) {
long lastPayload = getLastKeepAlivePayLoad();
PacketPlayInKeepAlive alive = (PacketPlayInKeepAlive) packetIn; PacketPlayInKeepAlive alive = (PacketPlayInKeepAlive) packetIn;
if (lastPayload == -1) {
Limbo.getInstance().getConsole().sendMessage("Unsolicited KeepAlive packet for player " + player.getName()); if (alive.getPayload() == getLastKeepAlivePayLoad()) {
} else if (alive.getPayload() != lastPayload) { lastKeepAliveResponse.set(System.currentTimeMillis());
Limbo.getInstance().getConsole().sendMessage("Incorrect Payload received in KeepAlive packet for player " + player.getName()); } else {
disconnect(Component.text("Bad Keepalive Payload"));
break; break;
} }
} else if (packetIn instanceof PacketPlayInTabComplete) { } else if (packetIn instanceof PacketPlayInTabComplete) {