Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib2PacketEvents</artifactId>
<version>1.0.6</version>
<version>1.0.7</version>
<packaging>jar</packaging>

<name>ProtocolLib2PacketEvents</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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());
Expand All @@ -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());
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
Loading