From 1cc3f120ee3f46e1fc5513c9988ff6370812f1ee Mon Sep 17 00:00:00 2001 From: Gero Date: Thu, 12 Oct 2023 11:11:47 +0200 Subject: [PATCH] Several improvements and fixes for 1.20.2 (#1097) * Send LoginAcknowledged immediately * Resend player list header/footer after backend server switched to config state * Fix clearHeaderAndFooter not clearing fields in ConnectedPlayer * Clear boss bars, header/footer, tab list when switching client to config state * Send client settings in config state --- .../com/velocitypowered/api/proxy/Player.java | 12 ++++++-- .../backend/ConfigSessionHandler.java | 15 ++++++---- .../backend/LoginSessionHandler.java | 28 +++++++++---------- .../backend/TransitionSessionHandler.java | 7 +++-- .../client/ClientPlaySessionHandler.java | 9 +++--- .../connection/client/ConnectedPlayer.java | 12 ++++++-- .../proxy/tablist/InternalTabList.java | 2 ++ .../proxy/tablist/KeyedVelocityTabList.java | 10 +++++-- .../proxy/tablist/VelocityTabList.java | 8 ++++-- .../proxy/tablist/VelocityTabListLegacy.java | 5 ++++ 10 files changed, 72 insertions(+), 36 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 9382fec22..99bf09da0 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -31,7 +31,6 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEventSource; import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; /** @@ -148,10 +147,17 @@ public interface Player extends /** * Clears the tab list header and footer for the player. * - * @deprecated Use {@link TabList#clearHeaderAndFooter()}. + * @deprecated Use {@link Player#clearPlayerListHeaderAndFooter()}. */ @Deprecated - void clearHeaderAndFooter(); + default void clearHeaderAndFooter() { + clearPlayerListHeaderAndFooter(); + } + + /** + * Clears the player list header and footer. + */ + void clearPlayerListHeaderAndFooter(); /** * Returns the player's player list header. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index d84ae49ce..7374f7985 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -24,6 +24,7 @@ import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; @@ -152,22 +153,26 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(FinishedUpdate packet) { MinecraftConnection smc = serverConn.ensureConnected(); + ConnectedPlayer player = serverConn.getPlayer(); ClientConfigSessionHandler configHandler = - (ClientConfigSessionHandler) serverConn.getPlayer().getConnection() - .getActiveSessionHandler(); + (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler(); smc.setAutoReading(false); // Even when not auto reading messages are still decoded. Decode them with the correct state smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY); configHandler.handleBackendFinishUpdate(serverConn).thenAcceptAsync((unused) -> { - if (serverConn == serverConn.getPlayer().getConnectedServer()) { + if (serverConn == player.getConnectedServer()) { smc.setActiveSessionHandler(StateRegistry.PLAY); + player.sendPlayerListHeaderAndFooter( + player.getPlayerListHeader(), player.getPlayerListFooter()); + // The client cleared the tab list. TODO: Restore changes done via TabList API + player.getTabList().clearAllSilent(); } else { smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture)); } - if (serverConn.getPlayer().getAppliedResourcePack() == null && resourcePackToApply != null) { - serverConn.getPlayer().queueResourcePack(resourcePackToApply); + if (player.getAppliedResourcePack() == null && resourcePackToApply != null) { + player.queueResourcePack(resourcePackToApply); } smc.setAutoReading(true); }, smc.eventLoop()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index e9801c315..10079db72 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -156,22 +156,20 @@ public class LoginSessionHandler implements MinecraftSessionHandler { smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture)); } else { - smc.setAutoReading(false); - CompletableFuture switchFuture; - if (serverConn.getPlayer().getConnection() - .getActiveSessionHandler() instanceof ClientPlaySessionHandler) { - switchFuture = ((ClientPlaySessionHandler) serverConn.getPlayer().getConnection() - .getActiveSessionHandler()).doSwitch(); - } else { - switchFuture = CompletableFuture.completedFuture(null); + smc.write(new LoginAcknowledged()); + smc.setActiveSessionHandler(StateRegistry.CONFIG, + new ConfigSessionHandler(server, serverConn, resultFuture)); + ConnectedPlayer player = serverConn.getPlayer(); + if (player.getClientSettingsPacket() != null) { + smc.write(player.getClientSettingsPacket()); + } + if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler) { + smc.setAutoReading(false); + ((ClientPlaySessionHandler) player.getConnection() + .getActiveSessionHandler()).doSwitch().thenAcceptAsync((unused) -> { + smc.setAutoReading(true); + }, smc.eventLoop()); } - switchFuture.thenAcceptAsync((unused) -> { - smc.write(new LoginAcknowledged()); - // Sync backend - smc.setActiveSessionHandler(StateRegistry.CONFIG, - new ConfigSessionHandler(server, serverConn, resultFuture)); - smc.setAutoReading(true); - }, smc.eventLoop()); } return true; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java index 30d1f4242..0f32fbefb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java @@ -22,6 +22,7 @@ import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeHands import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.event.player.ServerPostConnectEvent; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.ConnectionTypes; @@ -104,7 +105,7 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { player.sendKeepAlive(); // Reset Tablist header and footer to prevent desync - player.clearHeaderAndFooter(); + player.clearPlayerListHeaderAndFooter(); } // The goods are in hand! We got JoinGame. Let's transition completely to the new state. @@ -143,7 +144,9 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { // Now set the connected server. serverConn.getPlayer().setConnectedServer(serverConn); - if (player.getClientSettingsPacket() != null) { + // Send client settings. In 1.20.2+ this is done in the config state. + if (smc.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_20_2) < 0 + && player.getClientSettingsPacket() != null) { serverConn.ensureConnected().write(player.getClientSettingsPacket()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 304fbbaae..6004cf834 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -482,12 +482,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // Send keep alive to try to avoid timeouts player.sendKeepAlive(); - // Reset Tablist header and footer to prevent desync - player.clearHeaderAndFooter(); + // Config state clears everything in the client. No need to clear later. + spawned = false; + serverBossBars.clear(); + player.clearPlayerListHeaderAndFooterSilent(); + player.getTabList().clearAllSilent(); } - spawned = false; - player.switchToConfigState(); return configSwitchFuture; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index c922e933a..f7e67cc37 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -544,8 +544,16 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, } @Override - public void clearHeaderAndFooter() { - tabList.clearHeaderAndFooter(); + public void clearPlayerListHeaderAndFooter() { + clearPlayerListHeaderAndFooterSilent(); + if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + this.connection.write(HeaderAndFooter.reset()); + } + } + + public void clearPlayerListHeaderAndFooterSilent() { + this.playerListHeader = Component.empty(); + this.playerListFooter = Component.empty(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/InternalTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/InternalTabList.java index f3add2290..dcf00ecae 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/InternalTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/InternalTabList.java @@ -35,4 +35,6 @@ public interface InternalTabList extends TabList { default void processRemove(RemovePlayerInfo infoPacket) { } + + void clearAllSilent(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java index 63a9ae56f..e24c337ed 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java @@ -26,7 +26,6 @@ import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter; import com.velocitypowered.proxy.protocol.packet.LegacyPlayerListItem; import com.velocitypowered.proxy.protocol.packet.chat.RemoteChatSession; import java.util.ArrayList; @@ -70,7 +69,7 @@ public class KeyedVelocityTabList implements InternalTabList { @Override public void clearHeaderAndFooter() { - connection.write(HeaderAndFooter.reset()); + this.player.clearPlayerListHeaderAndFooter(); } @Override @@ -131,10 +130,15 @@ public class KeyedVelocityTabList implements InternalTabList { for (TabListEntry value : listEntries) { items.add(LegacyPlayerListItem.Item.from(value)); } - entries.clear(); + clearAllSilent(); connection.delayedWrite(new LegacyPlayerListItem(LegacyPlayerListItem.REMOVE_PLAYER, items)); } + @Override + public void clearAllSilent() { + entries.clear(); + } + @Override public Collection getEntries() { return Collections.unmodifiableCollection(this.entries.values()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index f707c95ca..4a95b00de 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -25,7 +25,6 @@ import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.console.VelocityConsole; -import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter; import com.velocitypowered.proxy.protocol.packet.RemovePlayerInfo; import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfo; import com.velocitypowered.proxy.protocol.packet.chat.RemoteChatSession; @@ -74,7 +73,7 @@ public class VelocityTabList implements InternalTabList { @Override public void clearHeaderAndFooter() { - connection.write(HeaderAndFooter.reset()); + this.player.clearPlayerListHeaderAndFooter(); } @Override @@ -175,6 +174,11 @@ public class VelocityTabList implements InternalTabList { @Override public void clearAll() { this.connection.delayedWrite(new RemovePlayerInfo(new ArrayList<>(this.entries.keySet()))); + clearAllSilent(); + } + + @Override + public void clearAllSilent() { this.entries.clear(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java index d23ac4cb5..6e1777788 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -71,6 +71,11 @@ public class VelocityTabListLegacy extends KeyedVelocityTabList { connection.delayedWrite(new LegacyPlayerListItem(LegacyPlayerListItem.REMOVE_PLAYER, Collections.singletonList(LegacyPlayerListItem.Item.from(value)))); } + clearAllSilent(); + } + + @Override + public void clearAllSilent() { entries.clear(); nameMapping.clear(); }