From 5712a7c3b9b18e9855655cf7f7e21f5ee3bd924b Mon Sep 17 00:00:00 2001 From: Namnarak Date: Tue, 13 Oct 2026 00:32:51 +0700 Subject: [PATCH] fix(login): route pre-login deferred packets via PacketEvents User channel --- CHANGELOG.md | 7 ++++ pom.xml | 2 +- .../internal/PacketEventsBackend.java | 39 +++++++++++++++++++ src/main/resources/plugin.yml | 4 +- .../PacketEventsBackendTemporaryUserTest.java | 32 +++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/comphenix/protocol/internal/PacketEventsBackendTemporaryUserTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index fbea6fe..c2eb23c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.7 — Deferred Pre-login Channel Fix + +- Fix deferred inbound `LOGIN.CLIENT.LOGIN_START` failure when ProtocolLib2PacketEvents had a generated `TemporaryPlayer` instead of a Bukkit `CraftPlayer`: use the already associated PacketEvents pre-login `User` and its live Netty Channel for silent transport instead of `PlayerManager.receivePacketSilently(Player, ...)`. +- Also route pre-login clientbound packets through the same User to avoid the analogous invalid CraftPlayer cast. +- Reject missing temporary User/channel rather than bypassing login authentication or swallowing the packet; ordinary CraftPlayer packet transport is unchanged. +- Add focused offline transport-selection regression tests; Maven test suite passes. + # Changelog All notable changes to ProtocolLib2PacketEvents (P2P) are documented here. diff --git a/pom.xml b/pom.xml index a24c168..b527563 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.comphenix.protocol ProtocolLib2PacketEvents - 1.0.6 + 1.0.7 jar ProtocolLib2PacketEvents diff --git a/src/main/java/com/comphenix/protocol/internal/PacketEventsBackend.java b/src/main/java/com/comphenix/protocol/internal/PacketEventsBackend.java index c5bce5f..adb0afa 100644 --- a/src/main/java/com/comphenix/protocol/internal/PacketEventsBackend.java +++ b/src/main/java/com/comphenix/protocol/internal/PacketEventsBackend.java @@ -2,6 +2,9 @@ import com.comphenix.protocol.events.PacketContainer; import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.player.User; +import com.comphenix.protocol.injector.temporary.TemporaryPlayer; +import com.comphenix.protocol.injector.temporary.TemporaryPlayerAdapter; import org.bukkit.entity.Player; /** PacketEvents transport for structured packets. */ @@ -18,6 +21,15 @@ private static void requireStructured(PacketContainer packet, String action) { public void send(Player player, PacketContainer packet, boolean filters) { requireStructured(packet, "send"); Object wrapper = packet.getPacketWrapper(); + User preLogin = temporaryTransport(player); + if (preLogin != null) { + if (wrapper != null) { + if (filters) preLogin.sendPacket(packet.getPacketWrapper()); + else preLogin.sendPacketSilently(packet.getPacketWrapper()); + } else if (filters) preLogin.sendPacket(packet.getHandle()); + else preLogin.sendPacketSilently(packet.getHandle()); + return; + } if (wrapper != null) { if (filters) PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet.getPacketWrapper()); else PacketEvents.getAPI().getPlayerManager().sendPacketSilently(player, packet.getPacketWrapper()); @@ -28,10 +40,37 @@ public void send(Player player, PacketContainer packet, boolean filters) { } } + /** + * A LOGIN/CONFIGURATION packet may have a ProtocolLib temporary player, + * not a Bukkit CraftPlayer. The PacketEvents PlayerManager casts its input + * to CraftPlayer; for temporary players the underlying pre-login User owns + * the real channel and must be used for transport. + * + * Fail loudly if its channel is missing, instead of silently discarding a + * credential-bearing LOGIN_START or bypassing authentication listeners. + */ + static User temporaryTransport(Player player) { + if (!(player instanceof TemporaryPlayer)) return null; + User user = TemporaryPlayerAdapter.getUser(player); + if (user == null || user.getChannel() == null) { + throw new IllegalStateException("Temporary ProtocolLib player has no PacketEvents login channel"); + } + return user; + } + @Override public void receive(Player player, PacketContainer packet, boolean filters) { requireStructured(packet, "receive"); Object wrapper = packet.getPacketWrapper(); + User preLogin = temporaryTransport(player); + if (preLogin != null) { + if (wrapper != null) { + if (filters) preLogin.receivePacket(packet.getPacketWrapper()); + else preLogin.receivePacketSilently(packet.getPacketWrapper()); + } else if (filters) preLogin.receivePacket(packet.getHandle()); + else preLogin.receivePacketSilently(packet.getHandle()); + return; + } if (wrapper != null) { if (filters) PacketEvents.getAPI().getPlayerManager().receivePacket(player, packet.getPacketWrapper()); else PacketEvents.getAPI().getPlayerManager().receivePacketSilently(player, packet.getPacketWrapper()); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c4e1b60..65267ba 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,11 +6,11 @@ main: com.comphenix.protocol.ProtocolLib # Bukkit.getPluginManager().getPlugin("ProtocolLib") - which resolves to this plugin # through `provides` below - and refuse to enable any ProtocolLib older than v5.0.0, # because ProtocolLib 4.x is genuinely incompatible with PacketEvents 2.x. P2P's real -# release version (1.0.6) is carried by the jar name and the GitHub releases. +# release version (1.0.7) is carried by the jar name and the GitHub releases. version: 5.4.0 author: CyoriaSMP Team description: A drop-in ProtocolLib compatibility layer powered by PacketEvents. -p2p-version: 1.0.6 +p2p-version: 1.0.7 protocol-lib-api-version: 5.4.0 depend: [packetevents] provides: [ProtocolLib] diff --git a/src/test/java/com/comphenix/protocol/internal/PacketEventsBackendTemporaryUserTest.java b/src/test/java/com/comphenix/protocol/internal/PacketEventsBackendTemporaryUserTest.java new file mode 100644 index 0000000..c62ec23 --- /dev/null +++ b/src/test/java/com/comphenix/protocol/internal/PacketEventsBackendTemporaryUserTest.java @@ -0,0 +1,32 @@ +package com.comphenix.protocol.internal; + +import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PacketEventsBackendTemporaryUserTest { + @Test + void fullyJoinedPlayerDoesNotUsePreLoginTransport() { + // The early guard is null-safe and the real-player path is untouched. + assertNull(PacketEventsBackend.temporaryTransport(null)); + } + + @Test + void anonymousTemporaryPlayerWithoutUserFailsClosed() { + Player facade = TemporaryPlayerFactory.createTemporaryPlayer(); + IllegalStateException error = assertThrows(IllegalStateException.class, + () -> PacketEventsBackend.temporaryTransport(facade)); + assertTrue(error.getMessage().contains("no PacketEvents login channel")); + } + + @Test + void temporaryTransportRejectsUnmappedFacadeWithoutTryingCraftPlayerCast() { + // This regression guard tests the pre-login failure mode without a + // running PacketEvents API (which User's constructor requires). + Player facade = TemporaryPlayerFactory.createTemporaryPlayer(); + assertThrows(IllegalStateException.class, + () -> PacketEventsBackend.temporaryTransport(facade)); + } +}