Describe the bug
When playing behind an NAT traversal tunnel (e.g., EasyTier-based Terracotta/陶瓦联机), the client crashes with a NullPointerException in ClientboundSableUDPActivationPacket.handle() because the UDP channel is null. This causes the client to be kicked with "Invalid player data" (无效的玩家数据).
The issue is client-side, distinct from #175 (server-side NPE in SableUDPServer.getServer()).
Steps to reproduce
- Use any tunneling/proxy tool that creates a virtual network interface (EasyTier, frp, ngrok, etc.)
- Join a server with Sable installed
- The server sends
sable:udp_activation → client's UDP channel is null → NPE
Crash log
[Render thread/INFO] [dev.ryanhcode.sable.Sable/]: Client UDP channel inactive
[Render thread/INFO] [dev.ryanhcode.sable.Sable/]: Closed UDP channel!
[Render thread/INFO] [dev.ryanhcode.sable.Sable/]: Received authentication request, sending response over UDP to localhost/127.0.0.1:25565
[Render thread/ERROR] [net.minecraft.util.thread.BlockableEventLoop/FATAL]: Error executing task on Client
java.util.concurrent.CompletionException: java.lang.NullPointerException: Cannot invoke "io.netty.channel.Channel.eventLoop()" because "channel" is null
at TRANSFORMER/sable@1.2.2/dev.ryanhcode.sable.network.packets.tcp.ClientboundSableUDPActivationPacket.handle(ClientboundSableUDPActivationPacket.java:51)
...
[Render thread/WARN] [net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl/]: Client disconnected with reason: 无效的玩家数据
Then the player reconnects and it crashes again identically.
Environment
- Mod version: sable-neoforge-1.21.1-1.2.2.jar
- Minecraft: 1.21.1 NeoForge
- Launcher: HMCL with Terracotta (EasyTier-based tunneling)
- Relevant config:
sable-common.toml
Root cause
ClientboundSableUDPActivationPacket.handle() (line 51) calls channel.eventLoop() without checking if channel is null. When the UDP channel fails to initialize (common in tunneled/VPN environments where the underlying Netty Bootstrap cannot bind properly), the field remains null and the handler crashes instead of falling back gracefully.
Workaround
Setting disable_udp_pipeline = true in sable-common.toml prevents the crash, but this disables UDP entirely for the affected client.
Suggested fix
Add a null guard at the top of ClientboundSableUDPActivationPacket.handle():
if (channel == null) {
LOGGER.warn("UDP channel is not available, skipping UDP activation");
return;
}
This allows the sub-level data to fall back to TCP without crashing the client.
Related
Describe the bug
When playing behind an NAT traversal tunnel (e.g., EasyTier-based Terracotta/陶瓦联机), the client crashes with a
NullPointerExceptioninClientboundSableUDPActivationPacket.handle()because the UDPchannelis null. This causes the client to be kicked with "Invalid player data" (无效的玩家数据).The issue is client-side, distinct from #175 (server-side NPE in
SableUDPServer.getServer()).Steps to reproduce
sable:udp_activation→ client's UDP channel is null → NPECrash log
Then the player reconnects and it crashes again identically.
Environment
sable-common.tomlRoot cause
ClientboundSableUDPActivationPacket.handle()(line 51) callschannel.eventLoop()without checking ifchannelis null. When the UDP channel fails to initialize (common in tunneled/VPN environments where the underlying Netty Bootstrap cannot bind properly), the field remains null and the handler crashes instead of falling back gracefully.Workaround
Setting
disable_udp_pipeline = trueinsable-common.tomlprevents the crash, but this disables UDP entirely for the affected client.Suggested fix
Add a null guard at the top of
ClientboundSableUDPActivationPacket.handle():This allows the sub-level data to fall back to TCP without crashing the client.
Related
SableUDPServer.getServer()returning null)