Connection.java

net.minecraft.network.Connection

信息

  • 全限定名:net.minecraft.network.Connection
  • 类型:public class
  • 包:net.minecraft.network
  • 源码路径:src/main/java/net/minecraft/network/Connection.java
  • 起始行号:L58
  • 继承:SimpleChannelInboundHandler<Packet<?>>
  • 职责:

    TODO

字段/常量

  • AVERAGE_PACKETS_SMOOTHING

    • 类型: float
    • 修饰符: private static final
    • 源码定位: L59
    • 说明:

      TODO

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L60
    • 说明:

      TODO

  • ROOT_MARKER

    • 类型: Marker
    • 修饰符: public static final
    • 源码定位: L61
    • 说明:

      TODO

  • PACKET_MARKER

    • 类型: Marker
    • 修饰符: public static final
    • 源码定位: L62
    • 说明:

      TODO

  • PACKET_RECEIVED_MARKER

    • 类型: Marker
    • 修饰符: public static final
    • 源码定位: L63
    • 说明:

      TODO

  • PACKET_SENT_MARKER

    • 类型: Marker
    • 修饰符: public static final
    • 源码定位: L64
    • 说明:

      TODO

  • INITIAL_PROTOCOL

    • 类型: ProtocolInfo<ServerHandshakePacketListener>
    • 修饰符: private static final
    • 源码定位: L65
    • 说明:

      TODO

  • receiving

    • 类型: PacketFlow
    • 修饰符: private final
    • 源码定位: L66
    • 说明:

      TODO

  • sendLoginDisconnect

    • 类型: boolean
    • 修饰符: private volatile
    • 源码定位: L67
    • 说明:

      TODO

  • pendingActions

    • 类型: Queue<Consumer<Connection>>
    • 修饰符: private final
    • 源码定位: L68
    • 说明:

      TODO

  • channel

    • 类型: Channel
    • 修饰符: private
    • 源码定位: L69
    • 说明:

      TODO

  • address

    • 类型: SocketAddress
    • 修饰符: private
    • 源码定位: L70
    • 说明:

      TODO

  • disconnectListener

    • 类型: PacketListener
    • 修饰符: private volatile
    • 源码定位: L71
    • 说明:

      TODO

  • packetListener

    • 类型: PacketListener
    • 修饰符: private volatile
    • 源码定位: L72
    • 说明:

      TODO

  • disconnectionDetails

    • 类型: DisconnectionDetails
    • 修饰符: private
    • 源码定位: L73
    • 说明:

      TODO

  • encrypted

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L74
    • 说明:

      TODO

  • disconnectionHandled

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L75
    • 说明:

      TODO

  • receivedPackets

    • 类型: int
    • 修饰符: private
    • 源码定位: L76
    • 说明:

      TODO

  • sentPackets

    • 类型: int
    • 修饰符: private
    • 源码定位: L77
    • 说明:

      TODO

  • averageReceivedPackets

    • 类型: float
    • 修饰符: private
    • 源码定位: L78
    • 说明:

      TODO

  • averageSentPackets

    • 类型: float
    • 修饰符: private
    • 源码定位: L79
    • 说明:

      TODO

  • tickCount

    • 类型: int
    • 修饰符: private
    • 源码定位: L80
    • 说明:

      TODO

  • handlingFault

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L81
    • 说明:

      TODO

  • delayedDisconnect

    • 类型: DisconnectionDetails
    • 修饰符: private volatile
    • 源码定位: L82
    • 说明:

      TODO

  • bandwidthDebugMonitor

    • 类型: BandwidthDebugMonitor
    • 修饰符: private
    • 源码定位: L83
    • 说明:

      TODO

内部类/嵌套类型

构造器

public Connection(PacketFlow receiving) @ L85

  • 构造器名:Connection
  • 源码定位:L85
  • 修饰符:public

参数:

  • receiving: PacketFlow

说明:

TODO

方法

下面的方法块按源码顺序生成。

public void channelActive(ChannelHandlerContext ctx) @ L89

  • 方法名:channelActive
  • 源码定位:L89
  • 返回类型:void
  • 修饰符:public

参数:

  • ctx: ChannelHandlerContext

说明:

TODO

public void channelInactive(ChannelHandlerContext ctx) @ L99

  • 方法名:channelInactive
  • 源码定位:L99
  • 返回类型:void
  • 修饰符:public

参数:

  • ctx: ChannelHandlerContext

说明:

TODO

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) @ L104

  • 方法名:exceptionCaught
  • 源码定位:L104
  • 返回类型:void
  • 修饰符:public

参数:

  • ctx: ChannelHandlerContext
  • cause: Throwable

说明:

TODO

protected void channelRead0(ChannelHandlerContext ctx, Packet<?> packet) @ L146

  • 方法名:channelRead0
  • 源码定位:L146
  • 返回类型:void
  • 修饰符:protected

参数:

  • ctx: ChannelHandlerContext
  • packet: Packet<?>

说明:

TODO

private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) @ L169

  • 方法名:genericsFtw
  • 源码定位:L169
  • 返回类型: void
  • 修饰符:private static

参数:

  • packet: Packet
  • listener: PacketListener

说明:

TODO

private void validateListener(ProtocolInfo<?> protocol, PacketListener packetListener) @ L173

  • 方法名:validateListener
  • 源码定位:L173
  • 返回类型:void
  • 修饰符:private

参数:

  • protocol: ProtocolInfo<?>
  • packetListener: PacketListener

说明:

TODO

private static void syncAfterConfigurationChange(ChannelFuture future) @ L186

  • 方法名:syncAfterConfigurationChange
  • 源码定位:L186
  • 返回类型:void
  • 修饰符:private static

参数:

  • future: ChannelFuture

说明:

TODO

public <T extends PacketListener> void setupInboundProtocol(ProtocolInfo<T> protocol, T packetListener) @ L198

  • 方法名:setupInboundProtocol
  • 源码定位:L198
  • 返回类型: void
  • 修饰符:public

参数:

  • protocol: ProtocolInfo
  • packetListener: T

说明:

TODO

public void setupOutboundProtocol(ProtocolInfo<?> protocol) @ L216

  • 方法名:setupOutboundProtocol
  • 源码定位:L216
  • 返回类型:void
  • 修饰符:public

参数:

  • protocol: ProtocolInfo<?>

说明:

TODO

public void setListenerForServerboundHandshake(PacketListener packetListener) @ L232

  • 方法名:setListenerForServerboundHandshake
  • 源码定位:L232
  • 返回类型:void
  • 修饰符:public

参数:

  • packetListener: PacketListener

说明:

TODO

public void initiateServerboundStatusConnection(String hostName, int port, ClientStatusPacketListener listener) @ L244

  • 方法名:initiateServerboundStatusConnection
  • 源码定位:L244
  • 返回类型:void
  • 修饰符:public

参数:

  • hostName: String
  • port: int
  • listener: ClientStatusPacketListener

说明:

TODO

public void initiateServerboundPlayConnection(String hostName, int port, ClientLoginPacketListener listener) @ L248

  • 方法名:initiateServerboundPlayConnection
  • 源码定位:L248
  • 返回类型:void
  • 修饰符:public

参数:

  • hostName: String
  • port: int
  • listener: ClientLoginPacketListener

说明:

TODO

public <S extends ServerboundPacketListener,C extends ClientboundPacketListener> void initiateServerboundPlayConnection(String hostName, int port, ProtocolInfo<S> outbound, ProtocolInfo<C> inbound, C listener, boolean transfer) @ L252

  • 方法名:initiateServerboundPlayConnection
  • 源码定位:L252
  • 返回类型:<S extends ServerboundPacketListener,C extends ClientboundPacketListener> void
  • 修饰符:public

参数:

  • hostName: String
  • port: int
  • outbound: ProtocolInfo
  • inbound: ProtocolInfo
  • listener: C
  • transfer: boolean

说明:

TODO

private <S extends ServerboundPacketListener,C extends ClientboundPacketListener> void initiateServerboundConnection(String hostName, int port, ProtocolInfo<S> outbound, ProtocolInfo<C> inbound, C listener, ClientIntent intent) @ L258

  • 方法名:initiateServerboundConnection
  • 源码定位:L258
  • 返回类型:<S extends ServerboundPacketListener,C extends ClientboundPacketListener> void
  • 修饰符:private

参数:

  • hostName: String
  • port: int
  • outbound: ProtocolInfo
  • inbound: ProtocolInfo
  • listener: C
  • intent: ClientIntent

说明:

TODO

public void send(Packet<?> packet) @ L273

  • 方法名:send
  • 源码定位:L273
  • 返回类型:void
  • 修饰符:public

参数:

  • packet: Packet<?>

说明:

TODO

public void send(Packet<?> packet, ChannelFutureListener listener) @ L277

  • 方法名:send
  • 源码定位:L277
  • 返回类型:void
  • 修饰符:public

参数:

  • packet: Packet<?>
  • listener: ChannelFutureListener

说明:

TODO

public void send(Packet<?> packet, ChannelFutureListener listener, boolean flush) @ L281

  • 方法名:send
  • 源码定位:L281
  • 返回类型:void
  • 修饰符:public

参数:

  • packet: Packet<?>
  • listener: ChannelFutureListener
  • flush: boolean

说明:

TODO

public void runOnceConnected(Consumer<Connection> action) @ L290

  • 方法名:runOnceConnected
  • 源码定位:L290
  • 返回类型:void
  • 修饰符:public

参数:

  • action: Consumer

说明:

TODO

private void sendPacket(Packet<?> packet, ChannelFutureListener listener, boolean flush) @ L299

  • 方法名:sendPacket
  • 源码定位:L299
  • 返回类型:void
  • 修饰符:private

参数:

  • packet: Packet<?>
  • listener: ChannelFutureListener
  • flush: boolean

说明:

TODO

private void doSendPacket(Packet<?> packet, ChannelFutureListener listener, boolean flush) @ L308

  • 方法名:doSendPacket
  • 源码定位:L308
  • 返回类型:void
  • 修饰符:private

参数:

  • packet: Packet<?>
  • listener: ChannelFutureListener
  • flush: boolean

说明:

TODO

public void flushChannel() @ L319

  • 方法名:flushChannel
  • 源码定位:L319
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

private void flush() @ L327

  • 方法名:flush
  • 源码定位:L327
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

private void flushQueue() @ L335

  • 方法名:flushQueue
  • 源码定位:L335
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public void tick() @ L346

  • 方法名:tick
  • 源码定位:L346
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

protected void tickSecond() @ L369

  • 方法名:tickSecond
  • 源码定位:L369
  • 返回类型:void
  • 修饰符:protected

参数:

说明:

TODO

public SocketAddress getRemoteAddress() @ L376

  • 方法名:getRemoteAddress
  • 源码定位:L376
  • 返回类型:SocketAddress
  • 修饰符:public

参数:

说明:

TODO

public String getLoggableAddress(boolean logIPs) @ L380

  • 方法名:getLoggableAddress
  • 源码定位:L380
  • 返回类型:String
  • 修饰符:public

参数:

  • logIPs: boolean

说明:

TODO

public void disconnect(Component reason) @ L388

  • 方法名:disconnect
  • 源码定位:L388
  • 返回类型:void
  • 修饰符:public

参数:

  • reason: Component

说明:

TODO

public void disconnect(DisconnectionDetails details) @ L392

  • 方法名:disconnect
  • 源码定位:L392
  • 返回类型:void
  • 修饰符:public

参数:

  • details: DisconnectionDetails

说明:

TODO

public boolean isMemoryConnection() @ L403

  • 方法名:isMemoryConnection
  • 源码定位:L403
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public PacketFlow getReceiving() @ L407

  • 方法名:getReceiving
  • 源码定位:L407
  • 返回类型:PacketFlow
  • 修饰符:public

参数:

说明:

TODO

public PacketFlow getSending() @ L411

  • 方法名:getSending
  • 源码定位:L411
  • 返回类型:PacketFlow
  • 修饰符:public

参数:

说明:

TODO

public static Connection connectToServer(InetSocketAddress address, EventLoopGroupHolder eventLoopGroupHolder, LocalSampleLogger bandwidthLogger) @ L415

  • 方法名:connectToServer
  • 源码定位:L415
  • 返回类型:Connection
  • 修饰符:public static

参数:

  • address: InetSocketAddress
  • eventLoopGroupHolder: EventLoopGroupHolder
  • bandwidthLogger: LocalSampleLogger

说明:

TODO

public static ChannelFuture connect(InetSocketAddress address, EventLoopGroupHolder eventLoopGroupHolder, Connection connection) @ L426

  • 方法名:connect
  • 源码定位:L426
  • 返回类型:ChannelFuture
  • 修饰符:public static

参数:

  • address: InetSocketAddress
  • eventLoopGroupHolder: EventLoopGroupHolder
  • connection: Connection

说明:

TODO

private static String outboundHandlerName(boolean configureOutbound) @ L442

  • 方法名:outboundHandlerName
  • 源码定位:L442
  • 返回类型:String
  • 修饰符:private static

参数:

  • configureOutbound: boolean

说明:

TODO

private static String inboundHandlerName(boolean configureInbound) @ L446

  • 方法名:inboundHandlerName
  • 源码定位:L446
  • 返回类型:String
  • 修饰符:private static

参数:

  • configureInbound: boolean

说明:

TODO

public void configurePacketHandler(ChannelPipeline pipeline) @ L450

  • 方法名:configurePacketHandler
  • 源码定位:L450
  • 返回类型:void
  • 修饰符:public

参数:

  • pipeline: ChannelPipeline

说明:

TODO

public static void configureSerialization(ChannelPipeline pipeline, PacketFlow inboundDirection, boolean local, BandwidthDebugMonitor monitor) @ L463

  • 方法名:configureSerialization
  • 源码定位:L463
  • 返回类型:void
  • 修饰符:public static

参数:

  • pipeline: ChannelPipeline
  • inboundDirection: PacketFlow
  • local: boolean
  • monitor: BandwidthDebugMonitor

说明:

TODO

private static ChannelOutboundHandler createFrameEncoder(boolean local) @ L480

  • 方法名:createFrameEncoder
  • 源码定位:L480
  • 返回类型:ChannelOutboundHandler
  • 修饰符:private static

参数:

  • local: boolean

说明:

TODO

private static ChannelInboundHandler createFrameDecoder(BandwidthDebugMonitor monitor, boolean local) @ L484

  • 方法名:createFrameDecoder
  • 源码定位:L484
  • 返回类型:ChannelInboundHandler
  • 修饰符:private static

参数:

  • monitor: BandwidthDebugMonitor
  • local: boolean

说明:

TODO

public static void configureInMemoryPipeline(ChannelPipeline pipeline, PacketFlow packetFlow) @ L492

  • 方法名:configureInMemoryPipeline
  • 源码定位:L492
  • 返回类型:void
  • 修饰符:public static

参数:

  • pipeline: ChannelPipeline
  • packetFlow: PacketFlow

说明:

TODO

public static Connection connectToLocalServer(SocketAddress address) @ L496

  • 方法名:connectToLocalServer
  • 源码定位:L496
  • 返回类型:Connection
  • 修饰符:public static

参数:

  • address: SocketAddress

说明:

TODO

public void setEncryptionKey(Cipher decryptCipher, Cipher encryptCipher) @ L509

  • 方法名:setEncryptionKey
  • 源码定位:L509
  • 返回类型:void
  • 修饰符:public

参数:

  • decryptCipher: Cipher
  • encryptCipher: Cipher

说明:

TODO

public boolean isEncrypted() @ L515

  • 方法名:isEncrypted
  • 源码定位:L515
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isConnected() @ L519

  • 方法名:isConnected
  • 源码定位:L519
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isConnecting() @ L523

  • 方法名:isConnecting
  • 源码定位:L523
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public PacketListener getPacketListener() @ L527

  • 方法名:getPacketListener
  • 源码定位:L527
  • 返回类型:PacketListener
  • 修饰符:public

参数:

说明:

TODO

public DisconnectionDetails getDisconnectionDetails() @ L531

  • 方法名:getDisconnectionDetails
  • 源码定位:L531
  • 返回类型:DisconnectionDetails
  • 修饰符:public

参数:

说明:

TODO

public void setReadOnly() @ L535

  • 方法名:setReadOnly
  • 源码定位:L535
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void setupCompression(int threshold, boolean validateDecompressed) @ L541

  • 方法名:setupCompression
  • 源码定位:L541
  • 返回类型:void
  • 修饰符:public

参数:

  • threshold: int
  • validateDecompressed: boolean

说明:

TODO

public void handleDisconnection() @ L565

  • 方法名:handleDisconnection
  • 源码定位:L565
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public float getAverageReceivedPackets() @ L583

  • 方法名:getAverageReceivedPackets
  • 源码定位:L583
  • 返回类型:float
  • 修饰符:public

参数:

说明:

TODO

public float getAverageSentPackets() @ L587

  • 方法名:getAverageSentPackets
  • 源码定位:L587
  • 返回类型:float
  • 修饰符:public

参数:

说明:

TODO

public void setBandwidthLogger(LocalSampleLogger bandwidthLogger) @ L591

  • 方法名:setBandwidthLogger
  • 源码定位:L591
  • 返回类型:void
  • 修饰符:public

参数:

  • bandwidthLogger: LocalSampleLogger

说明:

TODO

代码

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("NETWORK");
    public static final Marker PACKET_MARKER = Util.make(MarkerFactory.getMarker("NETWORK_PACKETS"), m -> m.add(ROOT_MARKER));
    public static final Marker PACKET_RECEIVED_MARKER = Util.make(MarkerFactory.getMarker("PACKET_RECEIVED"), m -> m.add(PACKET_MARKER));
    public static final Marker PACKET_SENT_MARKER = Util.make(MarkerFactory.getMarker("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;
    }
 
    @Override
    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);
        }
    }
 
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        this.disconnect(Component.translatable("disconnect.endOfStream"));
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        if (cause instanceof SkipPacketException) {
            LOGGER.debug("Skipping packet due to errors", cause.getCause());
        } else {
            boolean isFirstFault = !this.handlingFault;
            this.handlingFault = true;
            if (this.channel.isOpen()) {
                if (cause instanceof TimeoutException) {
                    LOGGER.debug("Timeout", cause);
                    this.disconnect(Component.translatable("disconnect.timeout"));
                } else {
                    Component reason = Component.translatable("disconnect.genericReason", "Internal Exception: " + cause);
                    PacketListener listener = this.packetListener;
                    DisconnectionDetails details;
                    if (listener != null) {
                        details = listener.createDisconnectionInfo(reason, cause);
                    } else {
                        details = new DisconnectionDetails(reason);
                    }
 
                    if (isFirstFault) {
                        LOGGER.debug("Failed to sent packet", cause);
                        if (this.getSending() == PacketFlow.CLIENTBOUND) {
                            Packet<?> packet = (Packet<?>)(this.sendLoginDisconnect
                                ? new ClientboundLoginDisconnectPacket(reason)
                                : new ClientboundDisconnectPacket(reason));
                            this.send(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()) {
            PacketListener packetListener = this.packetListener;
            if (packetListener == null) {
                throw new IllegalStateException("Received a packet before the packet listener was initialized");
            } else {
                if (packetListener.shouldHandleMessage(packet)) {
                    try {
                        genericsFtw(packet, packetListener);
                    } catch (RunningOnDifferentThreadException var5) {
                    } catch (RejectedExecutionException var6) {
                        this.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown"));
                    } catch (ClassCastException var7) {
                        LOGGER.error("Received {} that couldn't be processed", packet.getClass(), var7);
                        this.disconnect(Component.translatable("multiplayer.disconnect.invalid_packet"));
                    }
 
                    this.receivedPackets++;
                }
            }
        }
    }
 
    private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) {
        packet.handle((T)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 " + this.receiving + ", but listener is " + listenerFlow);
        } else {
            ConnectionProtocol listenerProtocol = packetListener.protocol();
            if (protocol.id() != listenerProtocol) {
                throw new IllegalStateException("Listener protocol (" + listenerProtocol + ") does not match requested one " + protocol);
            }
        }
    }
 
    private static void syncAfterConfigurationChange(ChannelFuture future) {
        try {
            future.syncUninterruptibly();
        } catch (Exception var2) {
            if (var2 instanceof ClosedChannelException) {
                LOGGER.info("Connection closed during protocol change");
            } else {
                throw var2;
            }
        }
    }
 
    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: " + protocol.id());
        } else {
            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", newBundler));
            }
 
            syncAfterConfigurationChange(this.channel.writeAndFlush(configMessage));
        }
    }
 
    public void setupOutboundProtocol(ProtocolInfo<?> protocol) {
        if (protocol.flow() != this.getSending()) {
            throw new IllegalStateException("Invalid outbound protocol: " + protocol.id());
        } else {
            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", newUnbundler));
            }
 
            boolean isLoginProtocol = protocol.id() == ConnectionProtocol.LOGIN;
            syncAfterConfigurationChange(this.channel.writeAndFlush(configMessage.andThen(ctx -> this.sendLoginDisconnect = isLoginProtocol)));
        }
    }
 
    public void setListenerForServerboundHandshake(PacketListener packetListener) {
        if (this.packetListener != null) {
            throw new IllegalStateException("Listener already set");
        } else if (this.receiving == PacketFlow.SERVERBOUND
            && packetListener.flow() == PacketFlow.SERVERBOUND
            && packetListener.protocol() == INITIAL_PROTOCOL.id()) {
            this.packetListener = packetListener;
        } else {
            throw new IllegalStateException("Invalid initial listener");
        }
    }
 
    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");
        } else {
            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(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());
        }
    }
 
    private void flushQueue() {
        if (this.channel != null && this.channel.isOpen()) {
            synchronized (this.pendingActions) {
                Consumer<Connection> pendingAction;
                while ((pendingAction = this.pendingActions.poll()) != null) {
                    pendingAction.accept(this);
                }
            }
        }
    }
 
    public void tick() {
        this.flushQueue();
        if (this.packetListener instanceof TickablePacketListener tickable) {
            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, (float)this.sentPackets, this.averageSentPackets);
        this.averageReceivedPackets = Mth.lerp(0.75F, (float)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";
        } else {
            return logIPs ? this.address.toString() : "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 = connect(address, eventLoopGroupHolder, connection);
        connect.syncUninterruptibly();
        return connection;
    }
 
    public static ChannelFuture connect(InetSocketAddress address, EventLoopGroupHolder eventLoopGroupHolder, Connection connection) {
        return new Bootstrap().group(eventLoopGroupHolder.eventLoopGroup()).handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel channel) {
                try {
                    channel.config().setOption(ChannelOption.TCP_NODELAY, true);
                } catch (ChannelException var3) {
                }
 
                ChannelPipeline pipeline = channel.pipeline().addLast("timeout", 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", new ChannelOutboundHandlerAdapter() {
            {
                Objects.requireNonNull(Connection.this);
            }
 
            @Override
            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                super.write(ctx, msg, promise);
            }
        }).addLast("packet_handler", 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", createFrameDecoder(monitor, local))
            .addLast(new FlowControlHandler())
            .addLast(
                inboundHandlerName(configureInbound),
                (ChannelHandler)(configureInbound ? new PacketDecoder<>(INITIAL_PROTOCOL) : new UnconfiguredPipelineHandler.Inbound())
            )
            .addLast("prepender", createFrameEncoder(local))
            .addLast(
                outboundHandlerName(configureOutbound),
                (ChannelHandler)(configureOutbound ? new PacketEncoder<>(INITIAL_PROTOCOL) : new UnconfiguredPipelineHandler.Outbound())
            );
    }
 
    private static ChannelOutboundHandler createFrameEncoder(boolean local) {
        return (ChannelOutboundHandler)(local ? new LocalFrameEncoder() : new Varint21LengthFieldPrepender());
    }
 
    private static ChannelInboundHandler createFrameDecoder(@Nullable BandwidthDebugMonitor monitor, boolean local) {
        if (!local) {
            return new Varint21FrameDecoder(monitor);
        } else {
            return (ChannelInboundHandler)(monitor != null ? new MonitoredLocalFrameDecoder(monitor) : new LocalFrameDecoder());
        }
    }
 
    public static void configureInMemoryPipeline(ChannelPipeline pipeline, PacketFlow packetFlow) {
        configureSerialization(pipeline, packetFlow, true, null);
    }
 
    public static Connection connectToLocalServer(SocketAddress address) {
        final Connection connection = new Connection(PacketFlow.CLIENTBOUND);
        new Bootstrap().group(EventLoopGroupHolder.local().eventLoopGroup()).handler(new ChannelInitializer<Channel>() {
            @Override
            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", new CipherDecoder(decryptCipher));
        this.channel.pipeline().addBefore("prepender", "encrypt", 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) {
            if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder compressionDecoder) {
                compressionDecoder.setThreshold(threshold, validateDecompressed);
            } else {
                this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(threshold, validateDecompressed));
            }
 
            if (this.channel.pipeline().get("compress") instanceof CompressionEncoder compressionEncoder) {
                compressionEncoder.setThreshold(threshold);
            } else {
                this.channel.pipeline().addAfter("prepender", "compress", 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() {
        if (this.channel != null && !this.channel.isOpen()) {
            if (this.disconnectionHandled) {
                LOGGER.warn("handleDisconnection() called twice");
            } else {
                this.disconnectionHandled = true;
                PacketListener packetListener = this.getPacketListener();
                PacketListener 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);
    }
}

引用的其他类