From 2cb8ba720cfdeb1b7c5af3c8c2627979c9e0845c Mon Sep 17 00:00:00 2001 From: Namnarak Date: Tue, 13 Oct 2026 01:38:03 +0700 Subject: [PATCH] fix(auth): resolve actual Netty Channel rather than Object networkManager --- CHANGELOG.md | 7 ++++ pom.xml | 2 +- .../protocol/reflect/FuzzyReflection.java | 7 +++- src/main/resources/plugin.yml | 2 +- .../reflect/NettyChannelFieldLookupTest.java | 33 +++++++++++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/comphenix/protocol/reflect/NettyChannelFieldLookupTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b13f176..d08f118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.11 — Strict Channel Field Resolution in FastLoginPlus Adapter + +- Fix `FuzzyReflection.getFieldValue(injector, Channel.class, true)` selecting the **preceding Object-typed `networkManager` field** instead of NettyChannelInjector's actual `Channel channel` field. The wrong bidirectional assignability check caused an inactive/null Channel or ClassCastException when FastLoginPlus examined a Java Premium connection during async Mojang session verification, then logged `disconnected during session verification` and timed the player out. +- Only match fields whose declared type is assignable to the requested class. This is the reflection rule that prevents unsafe casts; don't fall back to unrelated superclasses like `Object`. +- Add regression tests against the real NettyChannelInjector and a separate Object-before-Channel fixture. Both tests **fail on v1.0.10 and pass on v1.0.11**. +- Preserve all previous 1.0.7–1.0.10 transport/encryption/late-handler fixes and do not disable FastLoginPlus or AuthMe authentication. + ## 1.0.10 — Late-binding race during Java Premium login - Fix the v1.0.9 startup race where `NetworkManagerInjector.getTemporaryInjector` refuses to associate a valid active PacketEvents pre-login Channel with the generated `TemporaryPlayer` if native `packet_handler` has not appeared yet. FastLoginPlus thus loses its Channel for the whole verification session and aborts as disconnected. diff --git a/pom.xml b/pom.xml index 10ebda7..da12042 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.comphenix.protocol ProtocolLib2PacketEvents - 1.0.10 + 1.0.11 jar ProtocolLib2PacketEvents diff --git a/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index 4af4fb7..09d9820 100644 --- a/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -45,7 +45,12 @@ public static T getFieldValue(Object instance, Class fieldClass, boolean if (instance == null) throw new IllegalArgumentException("instance cannot be null"); for (Class current = instance.getClass(); current != null; current = current.getSuperclass()) { for (Field field : current.getDeclaredFields()) { - if (fieldClass.isAssignableFrom(field.getType()) || field.getType().isAssignableFrom(fieldClass)) { + // Only accept fields whose declared value can be safely read as + // fieldClass. The reverse assignability check incorrectly chose + // a preceding Object networkManager field when FastLoginPlus + // asked for io.netty.channel.Channel, returning null or throwing + // ClassCastException even though the real Channel was present. + if (fieldClass.isAssignableFrom(field.getType())) { try { if (forceAccess) field.setAccessible(true); return fieldClass.cast(field.get(instance)); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1bccbdf..f2687e0 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -10,7 +10,7 @@ main: com.comphenix.protocol.ProtocolLib version: 5.4.0 author: CyoriaSMP Team description: A drop-in ProtocolLib compatibility layer powered by PacketEvents. -p2p-version: 1.0.10 +p2p-version: 1.0.11 protocol-lib-api-version: 5.4.0 depend: [packetevents] provides: [ProtocolLib] diff --git a/src/test/java/com/comphenix/protocol/reflect/NettyChannelFieldLookupTest.java b/src/test/java/com/comphenix/protocol/reflect/NettyChannelFieldLookupTest.java new file mode 100644 index 0000000..fd4a735 --- /dev/null +++ b/src/test/java/com/comphenix/protocol/reflect/NettyChannelFieldLookupTest.java @@ -0,0 +1,33 @@ +package com.comphenix.protocol.reflect; + +import com.comphenix.protocol.injector.netty.channel.NettyChannelInjector; +import io.netty.channel.Channel; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class NettyChannelFieldLookupTest { + private static final class OldFieldOrder { + private final Object networkManager = new Object(); + private final Channel channel; + OldFieldOrder(Channel channel) { this.channel = channel; } + } + + @Test + void picksExactChannelInsteadOfObjectWithNativeConnection() { + EmbeddedChannel channel = new EmbeddedChannel(); + try { + assertSame(channel, FuzzyReflection.getFieldValue(new OldFieldOrder(channel), Channel.class, true)); + } finally { channel.finishAndReleaseAll(); } + } + + @Test + void realNettyInjectorReturnsChannelWhenNativeConnectionNotReady() { + EmbeddedChannel channel = new EmbeddedChannel(); + try { + NettyChannelInjector injector = new NettyChannelInjector(null, null, channel, null, null, null); + assertSame(channel, FuzzyReflection.getFieldValue(injector, Channel.class, true)); + } finally { channel.finishAndReleaseAll(); } + } +}