2025-11-24 22:52:51 +03:00

631 lines
28 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.Queues
* com.mojang.logging.LogUtils
* io.netty.bootstrap.Bootstrap
* io.netty.channel.Channel
* io.netty.channel.ChannelException
* io.netty.channel.ChannelFuture
* io.netty.channel.ChannelFutureListener
* io.netty.channel.ChannelHandler
* io.netty.channel.ChannelHandlerContext
* io.netty.channel.ChannelInboundHandler
* io.netty.channel.ChannelInitializer
* io.netty.channel.ChannelOption
* io.netty.channel.ChannelOutboundHandler
* io.netty.channel.ChannelOutboundHandlerAdapter
* io.netty.channel.ChannelPipeline
* io.netty.channel.ChannelPromise
* io.netty.channel.SimpleChannelInboundHandler
* io.netty.channel.local.LocalChannel
* io.netty.channel.local.LocalServerChannel
* io.netty.handler.flow.FlowControlHandler
* io.netty.handler.timeout.ReadTimeoutHandler
* io.netty.handler.timeout.TimeoutException
* io.netty.util.concurrent.GenericFutureListener
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
* org.slf4j.Marker
* org.slf4j.MarkerFactory
*/
package net.minecraft.network;
import com.google.common.collect.Queues;
import com.mojang.logging.LogUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
import io.netty.handler.flow.FlowControlHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.TimeoutException;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.ClosedChannelException;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.RejectedExecutionException;
import java.util.function.Consumer;
import javax.crypto.Cipher;
import net.minecraft.SharedConstants;
import net.minecraft.network.BandwidthDebugMonitor;
import net.minecraft.network.CipherDecoder;
import net.minecraft.network.CipherEncoder;
import net.minecraft.network.ClientboundPacketListener;
import net.minecraft.network.CompressionDecoder;
import net.minecraft.network.CompressionEncoder;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.DisconnectionDetails;
import net.minecraft.network.LocalFrameDecoder;
import net.minecraft.network.LocalFrameEncoder;
import net.minecraft.network.MonitoredLocalFrameDecoder;
import net.minecraft.network.PacketBundlePacker;
import net.minecraft.network.PacketBundleUnpacker;
import net.minecraft.network.PacketDecoder;
import net.minecraft.network.PacketEncoder;
import net.minecraft.network.PacketListener;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.ProtocolInfo;
import net.minecraft.network.ServerboundPacketListener;
import net.minecraft.network.SkipPacketException;
import net.minecraft.network.TickablePacketListener;
import net.minecraft.network.UnconfiguredPipelineHandler;
import net.minecraft.network.Varint21FrameDecoder;
import net.minecraft.network.Varint21LengthFieldPrepender;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.BundlerInfo;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.common.ClientboundDisconnectPacket;
import net.minecraft.network.protocol.handshake.ClientIntent;
import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
import net.minecraft.network.protocol.handshake.HandshakeProtocols;
import net.minecraft.network.protocol.handshake.ServerHandshakePacketListener;
import net.minecraft.network.protocol.login.ClientLoginPacketListener;
import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket;
import net.minecraft.network.protocol.login.LoginProtocols;
import net.minecraft.network.protocol.status.ClientStatusPacketListener;
import net.minecraft.network.protocol.status.StatusProtocols;
import net.minecraft.server.RunningOnDifferentThreadException;
import net.minecraft.server.network.EventLoopGroupHolder;
import net.minecraft.util.Mth;
import net.minecraft.util.Util;
import net.minecraft.util.debugchart.LocalSampleLogger;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class Connection
extends SimpleChannelInboundHandler<Packet<?>> {
private static final float AVERAGE_PACKETS_SMOOTHING = 0.75f;
private static final Logger LOGGER = LogUtils.getLogger();
public static final Marker ROOT_MARKER = MarkerFactory.getMarker((String)"NETWORK");
public static final Marker PACKET_MARKER = Util.make(MarkerFactory.getMarker((String)"NETWORK_PACKETS"), m -> m.add(ROOT_MARKER));
public static final Marker PACKET_RECEIVED_MARKER = Util.make(MarkerFactory.getMarker((String)"PACKET_RECEIVED"), m -> m.add(PACKET_MARKER));
public static final Marker PACKET_SENT_MARKER = Util.make(MarkerFactory.getMarker((String)"PACKET_SENT"), m -> m.add(PACKET_MARKER));
private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
private final PacketFlow receiving;
private volatile boolean sendLoginDisconnect = true;
private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
private Channel channel;
private SocketAddress address;
private volatile @Nullable PacketListener disconnectListener;
private volatile @Nullable PacketListener packetListener;
private @Nullable DisconnectionDetails disconnectionDetails;
private boolean encrypted;
private boolean disconnectionHandled;
private int receivedPackets;
private int sentPackets;
private float averageReceivedPackets;
private float averageSentPackets;
private int tickCount;
private boolean handlingFault;
private volatile @Nullable DisconnectionDetails delayedDisconnect;
private @Nullable BandwidthDebugMonitor bandwidthDebugMonitor;
public Connection(PacketFlow receiving) {
this.receiving = receiving;
}
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
this.channel = ctx.channel();
this.address = this.channel.remoteAddress();
if (this.delayedDisconnect != null) {
this.disconnect(this.delayedDisconnect);
}
}
public void channelInactive(ChannelHandlerContext ctx) {
this.disconnect(Component.translatable("disconnect.endOfStream"));
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof SkipPacketException) {
LOGGER.debug("Skipping packet due to errors", cause.getCause());
return;
}
boolean isFirstFault = !this.handlingFault;
this.handlingFault = true;
if (!this.channel.isOpen()) {
return;
}
if (cause instanceof TimeoutException) {
LOGGER.debug("Timeout", cause);
this.disconnect(Component.translatable("disconnect.timeout"));
} else {
MutableComponent reason = Component.translatable("disconnect.genericReason", "Internal Exception: " + String.valueOf(cause));
PacketListener listener = this.packetListener;
DisconnectionDetails details = listener != null ? listener.createDisconnectionInfo(reason, cause) : new DisconnectionDetails(reason);
if (isFirstFault) {
LOGGER.debug("Failed to sent packet", cause);
if (this.getSending() == PacketFlow.CLIENTBOUND) {
Record packet = this.sendLoginDisconnect ? new ClientboundLoginDisconnectPacket(reason) : new ClientboundDisconnectPacket(reason);
this.send((Packet<?>)((Object)packet), PacketSendListener.thenRun(() -> this.disconnect(details)));
} else {
this.disconnect(details);
}
this.setReadOnly();
} else {
LOGGER.debug("Double fault", cause);
this.disconnect(details);
}
}
}
protected void channelRead0(ChannelHandlerContext ctx, Packet<?> packet) {
if (!this.channel.isOpen()) {
return;
}
PacketListener packetListener = this.packetListener;
if (packetListener == null) {
throw new IllegalStateException("Received a packet before the packet listener was initialized");
}
if (packetListener.shouldHandleMessage(packet)) {
try {
Connection.genericsFtw(packet, packetListener);
}
catch (RunningOnDifferentThreadException runningOnDifferentThreadException) {
}
catch (RejectedExecutionException ignored) {
this.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown"));
}
catch (ClassCastException exception) {
LOGGER.error("Received {} that couldn't be processed", packet.getClass(), (Object)exception);
this.disconnect(Component.translatable("multiplayer.disconnect.invalid_packet"));
}
++this.receivedPackets;
}
}
private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) {
packet.handle(listener);
}
private void validateListener(ProtocolInfo<?> protocol, PacketListener packetListener) {
Objects.requireNonNull(packetListener, "packetListener");
PacketFlow listenerFlow = packetListener.flow();
if (listenerFlow != this.receiving) {
throw new IllegalStateException("Trying to set listener for wrong side: connection is " + String.valueOf((Object)this.receiving) + ", but listener is " + String.valueOf((Object)listenerFlow));
}
ConnectionProtocol listenerProtocol = packetListener.protocol();
if (protocol.id() != listenerProtocol) {
throw new IllegalStateException("Listener protocol (" + String.valueOf((Object)listenerProtocol) + ") does not match requested one " + String.valueOf(protocol));
}
}
private static void syncAfterConfigurationChange(ChannelFuture future) {
try {
future.syncUninterruptibly();
}
catch (Exception e) {
if (e instanceof ClosedChannelException) {
LOGGER.info("Connection closed during protocol change");
return;
}
throw e;
}
}
public <T extends PacketListener> void setupInboundProtocol(ProtocolInfo<T> protocol, T packetListener) {
this.validateListener(protocol, packetListener);
if (protocol.flow() != this.getReceiving()) {
throw new IllegalStateException("Invalid inbound protocol: " + String.valueOf((Object)protocol.id()));
}
this.packetListener = packetListener;
this.disconnectListener = null;
UnconfiguredPipelineHandler.InboundConfigurationTask configMessage = UnconfiguredPipelineHandler.setupInboundProtocol(protocol);
BundlerInfo bundlerInfo = protocol.bundlerInfo();
if (bundlerInfo != null) {
PacketBundlePacker newBundler = new PacketBundlePacker(bundlerInfo);
configMessage = configMessage.andThen(ctx -> ctx.pipeline().addAfter("decoder", "bundler", (ChannelHandler)newBundler));
}
Connection.syncAfterConfigurationChange(this.channel.writeAndFlush((Object)configMessage));
}
public void setupOutboundProtocol(ProtocolInfo<?> protocol) {
if (protocol.flow() != this.getSending()) {
throw new IllegalStateException("Invalid outbound protocol: " + String.valueOf((Object)protocol.id()));
}
UnconfiguredPipelineHandler.OutboundConfigurationTask configMessage = UnconfiguredPipelineHandler.setupOutboundProtocol(protocol);
BundlerInfo bundlerInfo = protocol.bundlerInfo();
if (bundlerInfo != null) {
PacketBundleUnpacker newUnbundler = new PacketBundleUnpacker(bundlerInfo);
configMessage = configMessage.andThen(ctx -> ctx.pipeline().addAfter("encoder", "unbundler", (ChannelHandler)newUnbundler));
}
boolean isLoginProtocol = protocol.id() == ConnectionProtocol.LOGIN;
Connection.syncAfterConfigurationChange(this.channel.writeAndFlush((Object)configMessage.andThen(ctx -> {
this.sendLoginDisconnect = isLoginProtocol;
})));
}
public void setListenerForServerboundHandshake(PacketListener packetListener) {
if (this.packetListener != null) {
throw new IllegalStateException("Listener already set");
}
if (this.receiving != PacketFlow.SERVERBOUND || packetListener.flow() != PacketFlow.SERVERBOUND || packetListener.protocol() != INITIAL_PROTOCOL.id()) {
throw new IllegalStateException("Invalid initial listener");
}
this.packetListener = packetListener;
}
public void initiateServerboundStatusConnection(String hostName, int port, ClientStatusPacketListener listener) {
this.initiateServerboundConnection(hostName, port, StatusProtocols.SERVERBOUND, StatusProtocols.CLIENTBOUND, listener, ClientIntent.STATUS);
}
public void initiateServerboundPlayConnection(String hostName, int port, ClientLoginPacketListener listener) {
this.initiateServerboundConnection(hostName, port, LoginProtocols.SERVERBOUND, LoginProtocols.CLIENTBOUND, listener, ClientIntent.LOGIN);
}
public <S extends ServerboundPacketListener, C extends ClientboundPacketListener> void initiateServerboundPlayConnection(String hostName, int port, ProtocolInfo<S> outbound, ProtocolInfo<C> inbound, C listener, boolean transfer) {
this.initiateServerboundConnection(hostName, port, outbound, inbound, listener, transfer ? ClientIntent.TRANSFER : ClientIntent.LOGIN);
}
private <S extends ServerboundPacketListener, C extends ClientboundPacketListener> void initiateServerboundConnection(String hostName, int port, ProtocolInfo<S> outbound, ProtocolInfo<C> inbound, C listener, ClientIntent intent) {
if (outbound.id() != inbound.id()) {
throw new IllegalStateException("Mismatched initial protocols");
}
this.disconnectListener = listener;
this.runOnceConnected(connection -> {
this.setupInboundProtocol(inbound, listener);
connection.sendPacket(new ClientIntentionPacket(SharedConstants.getCurrentVersion().protocolVersion(), hostName, port, intent), null, true);
this.setupOutboundProtocol(outbound);
});
}
public void send(Packet<?> packet) {
this.send(packet, null);
}
public void send(Packet<?> packet, @Nullable ChannelFutureListener listener) {
this.send(packet, listener, true);
}
public void send(Packet<?> packet, @Nullable ChannelFutureListener listener, boolean flush) {
if (this.isConnected()) {
this.flushQueue();
this.sendPacket(packet, listener, flush);
} else {
this.pendingActions.add(connection -> connection.sendPacket(packet, listener, flush));
}
}
public void runOnceConnected(Consumer<Connection> action) {
if (this.isConnected()) {
this.flushQueue();
action.accept(this);
} else {
this.pendingActions.add(action);
}
}
private void sendPacket(Packet<?> packet, @Nullable ChannelFutureListener listener, boolean flush) {
++this.sentPackets;
if (this.channel.eventLoop().inEventLoop()) {
this.doSendPacket(packet, listener, flush);
} else {
this.channel.eventLoop().execute(() -> this.doSendPacket(packet, listener, flush));
}
}
private void doSendPacket(Packet<?> packet, @Nullable ChannelFutureListener listener, boolean flush) {
if (listener != null) {
ChannelFuture future = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
future.addListener((GenericFutureListener)listener);
} else if (flush) {
this.channel.writeAndFlush(packet, this.channel.voidPromise());
} else {
this.channel.write(packet, this.channel.voidPromise());
}
}
public void flushChannel() {
if (this.isConnected()) {
this.flush();
} else {
this.pendingActions.add(Connection::flush);
}
}
private void flush() {
if (this.channel.eventLoop().inEventLoop()) {
this.channel.flush();
} else {
this.channel.eventLoop().execute(() -> this.channel.flush());
}
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
private void flushQueue() {
if (this.channel == null || !this.channel.isOpen()) {
return;
}
Queue<Consumer<Connection>> queue = this.pendingActions;
synchronized (queue) {
Consumer<Connection> pendingAction;
while ((pendingAction = this.pendingActions.poll()) != null) {
pendingAction.accept(this);
}
}
}
public void tick() {
this.flushQueue();
PacketListener packetListener = this.packetListener;
if (packetListener instanceof TickablePacketListener) {
TickablePacketListener tickable = (TickablePacketListener)packetListener;
tickable.tick();
}
if (!this.isConnected() && !this.disconnectionHandled) {
this.handleDisconnection();
}
if (this.channel != null) {
this.channel.flush();
}
if (this.tickCount++ % 20 == 0) {
this.tickSecond();
}
if (this.bandwidthDebugMonitor != null) {
this.bandwidthDebugMonitor.tick();
}
}
protected void tickSecond() {
this.averageSentPackets = Mth.lerp(0.75f, this.sentPackets, this.averageSentPackets);
this.averageReceivedPackets = Mth.lerp(0.75f, this.receivedPackets, this.averageReceivedPackets);
this.sentPackets = 0;
this.receivedPackets = 0;
}
public SocketAddress getRemoteAddress() {
return this.address;
}
public String getLoggableAddress(boolean logIPs) {
if (this.address == null) {
return "local";
}
if (logIPs) {
return this.address.toString();
}
return "IP hidden";
}
public void disconnect(Component reason) {
this.disconnect(new DisconnectionDetails(reason));
}
public void disconnect(DisconnectionDetails details) {
if (this.channel == null) {
this.delayedDisconnect = details;
}
if (this.isConnected()) {
this.channel.close().awaitUninterruptibly();
this.disconnectionDetails = details;
}
}
public boolean isMemoryConnection() {
return this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel;
}
public PacketFlow getReceiving() {
return this.receiving;
}
public PacketFlow getSending() {
return this.receiving.getOpposite();
}
public static Connection connectToServer(InetSocketAddress address, EventLoopGroupHolder eventLoopGroupHolder, @Nullable LocalSampleLogger bandwidthLogger) {
Connection connection = new Connection(PacketFlow.CLIENTBOUND);
if (bandwidthLogger != null) {
connection.setBandwidthLogger(bandwidthLogger);
}
ChannelFuture connect = Connection.connect(address, eventLoopGroupHolder, connection);
connect.syncUninterruptibly();
return connection;
}
public static ChannelFuture connect(InetSocketAddress address, EventLoopGroupHolder eventLoopGroupHolder, final Connection connection) {
return ((Bootstrap)((Bootstrap)((Bootstrap)new Bootstrap().group(eventLoopGroupHolder.eventLoopGroup())).handler((ChannelHandler)new ChannelInitializer<Channel>(){
protected void initChannel(Channel channel) {
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, (Object)true);
}
catch (ChannelException channelException) {
// empty catch block
}
ChannelPipeline pipeline = channel.pipeline().addLast("timeout", (ChannelHandler)new ReadTimeoutHandler(30));
Connection.configureSerialization(pipeline, PacketFlow.CLIENTBOUND, false, connection.bandwidthDebugMonitor);
connection.configurePacketHandler(pipeline);
}
})).channel(eventLoopGroupHolder.channelCls())).connect(address.getAddress(), address.getPort());
}
private static String outboundHandlerName(boolean configureOutbound) {
return configureOutbound ? "encoder" : "outbound_config";
}
private static String inboundHandlerName(boolean configureInbound) {
return configureInbound ? "decoder" : "inbound_config";
}
public void configurePacketHandler(ChannelPipeline pipeline) {
pipeline.addLast("hackfix", (ChannelHandler)new ChannelOutboundHandlerAdapter(this){
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
super.write(ctx, msg, promise);
}
}).addLast("packet_handler", (ChannelHandler)this);
}
public static void configureSerialization(ChannelPipeline pipeline, PacketFlow inboundDirection, boolean local, @Nullable BandwidthDebugMonitor monitor) {
PacketFlow outboundDirection = inboundDirection.getOpposite();
boolean configureInbound = inboundDirection == PacketFlow.SERVERBOUND;
boolean configureOutbound = outboundDirection == PacketFlow.SERVERBOUND;
pipeline.addLast("splitter", (ChannelHandler)Connection.createFrameDecoder(monitor, local)).addLast(new ChannelHandler[]{new FlowControlHandler()}).addLast(Connection.inboundHandlerName(configureInbound), configureInbound ? new PacketDecoder<ServerHandshakePacketListener>(INITIAL_PROTOCOL) : new UnconfiguredPipelineHandler.Inbound()).addLast("prepender", (ChannelHandler)Connection.createFrameEncoder(local)).addLast(Connection.outboundHandlerName(configureOutbound), configureOutbound ? new PacketEncoder<ServerHandshakePacketListener>(INITIAL_PROTOCOL) : new UnconfiguredPipelineHandler.Outbound());
}
private static ChannelOutboundHandler createFrameEncoder(boolean local) {
return local ? new LocalFrameEncoder() : new Varint21LengthFieldPrepender();
}
private static ChannelInboundHandler createFrameDecoder(@Nullable BandwidthDebugMonitor monitor, boolean local) {
if (!local) {
return new Varint21FrameDecoder(monitor);
}
if (monitor != null) {
return new MonitoredLocalFrameDecoder(monitor);
}
return new LocalFrameDecoder();
}
public static void configureInMemoryPipeline(ChannelPipeline pipeline, PacketFlow packetFlow) {
Connection.configureSerialization(pipeline, packetFlow, true, null);
}
public static Connection connectToLocalServer(SocketAddress address) {
final Connection connection = new Connection(PacketFlow.CLIENTBOUND);
((Bootstrap)((Bootstrap)((Bootstrap)new Bootstrap().group(EventLoopGroupHolder.local().eventLoopGroup())).handler((ChannelHandler)new ChannelInitializer<Channel>(){
protected void initChannel(Channel channel) {
ChannelPipeline pipeline = channel.pipeline();
Connection.configureInMemoryPipeline(pipeline, PacketFlow.CLIENTBOUND);
connection.configurePacketHandler(pipeline);
}
})).channel(EventLoopGroupHolder.local().channelCls())).connect(address).syncUninterruptibly();
return connection;
}
public void setEncryptionKey(Cipher decryptCipher, Cipher encryptCipher) {
this.encrypted = true;
this.channel.pipeline().addBefore("splitter", "decrypt", (ChannelHandler)new CipherDecoder(decryptCipher));
this.channel.pipeline().addBefore("prepender", "encrypt", (ChannelHandler)new CipherEncoder(encryptCipher));
}
public boolean isEncrypted() {
return this.encrypted;
}
public boolean isConnected() {
return this.channel != null && this.channel.isOpen();
}
public boolean isConnecting() {
return this.channel == null;
}
public @Nullable PacketListener getPacketListener() {
return this.packetListener;
}
public @Nullable DisconnectionDetails getDisconnectionDetails() {
return this.disconnectionDetails;
}
public void setReadOnly() {
if (this.channel != null) {
this.channel.config().setAutoRead(false);
}
}
public void setupCompression(int threshold, boolean validateDecompressed) {
if (threshold >= 0) {
ChannelHandler channelHandler = this.channel.pipeline().get("decompress");
if (channelHandler instanceof CompressionDecoder) {
CompressionDecoder compressionDecoder = (CompressionDecoder)channelHandler;
compressionDecoder.setThreshold(threshold, validateDecompressed);
} else {
this.channel.pipeline().addAfter("splitter", "decompress", (ChannelHandler)new CompressionDecoder(threshold, validateDecompressed));
}
channelHandler = this.channel.pipeline().get("compress");
if (channelHandler instanceof CompressionEncoder) {
CompressionEncoder compressionEncoder = (CompressionEncoder)channelHandler;
compressionEncoder.setThreshold(threshold);
} else {
this.channel.pipeline().addAfter("prepender", "compress", (ChannelHandler)new CompressionEncoder(threshold));
}
} else {
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
this.channel.pipeline().remove("decompress");
}
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
this.channel.pipeline().remove("compress");
}
}
}
public void handleDisconnection() {
PacketListener disconnectListener;
if (this.channel == null || this.channel.isOpen()) {
return;
}
if (this.disconnectionHandled) {
LOGGER.warn("handleDisconnection() called twice");
return;
}
this.disconnectionHandled = true;
PacketListener packetListener = this.getPacketListener();
PacketListener packetListener2 = disconnectListener = packetListener != null ? packetListener : this.disconnectListener;
if (disconnectListener != null) {
DisconnectionDetails details = Objects.requireNonNullElseGet(this.getDisconnectionDetails(), () -> new DisconnectionDetails(Component.translatable("multiplayer.disconnect.generic")));
disconnectListener.onDisconnect(details);
}
}
public float getAverageReceivedPackets() {
return this.averageReceivedPackets;
}
public float getAverageSentPackets() {
return this.averageSentPackets;
}
public void setBandwidthLogger(LocalSampleLogger bandwidthLogger) {
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwidthLogger);
}
}