QuicheConnection class

Пакет: com.hypixel.hytale.lib.quiche

Файл: com/hypixel/hytale/lib/quiche/QuicheConnection.java

Поля (111)

МодификаторыТипИмя
final HytaleLogger LOGGER
break
break
break
break
break
break
continue
continue
continue
continue
continue
Long2ObjectFunction var1
QuicheListener.DatagramBuffer var1
int var10
var10
MemorySegment var105
QuicheChannel var106
QuicheListener.DatagramBuffer var109
MemorySegment var11
String var11
var11
QuicheChannel var110
QuicheChannel var111
int var112
long var113
long var115
MemorySegment var116
MemorySegment var117
MemorySegment var118
long var119
var119
MemorySegment var12
MemorySegment var12
MemorySegment var120
int var121
var121
long var122
ObjectIterator var123
long var124
QuicheChannel var125
long var126
long var127
long var128
MemorySegment var129
MemorySegment var13
MemorySegment var13
boolean var14
var14
long var14
boolean var15
var15
var15
var15
var15
boolean var15
Runnable var16
long var17
QuicheChannel var19
long var2
MemorySegment var2
MemorySegment var2
FormattedMessage var2
MemorySegment var2
int var2
long var20
long var21
long var22
int var23
ObjectIterator var24
QuicheChannel var25
CertificateFactory var26
MemorySegment var27
MemorySegment var28
long var29
QuicheChannel var3
MemorySegment var3
MemorySegment var3
ByteBuffer var3
long var3
QuicheChannel var3
long var30
MemorySegment var31
MemorySegment var32
MemorySegment var33
ObjectIterator var36
QuicheChannel var37
QuicheConfig.RateLimit var4
MemorySegment var4
BiFunction var4
long var4
MemorySegment var4
MemorySegment var4
MemorySegment var5
QuicheChannel var5
MemorySegment var5
MemorySegment var6
ConnectionHandler var6
MemorySegment var6
long var6
long var6
ByteBuffer var7
int var7
String var7
InetSocketAddress var8
var8
FormattedMessage var8
boolean var8
InetSocketAddress var8
MemorySegment var9
long var9

Методы (48)

МодификаторыВозвратСигнатура
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("Cannot create server-initiated uni stream for " + var1)
abstract throw new IllegalStateExceptionthrow new IllegalStateException("Failed to send all bytes")
QuicheChannel createChannelQuicheChannel createChannel(NetworkChannel var1)
private void drainSendQueuevoid drainSendQueue()
public void executevoid execute(Runnable var1)
for for(int var5 = 0; var5 < 100; var5++)
private int getNegotiatedAlpnVersionint getNegotiatedAlpnVersion()
private void gracefulClosevoid gracefulClose(QuicheChannel var1)
if if(var19.isNew)
if if(var20 < 0L)
if if(var20 != -1L)
if if(var110 != null)
if if(var111 == null)
if if(var21 < 0L)
if if(var113 < 0L)
if if(var113 != -1L)
if if(var113 == 0L)
if if(var121 != var113)
if if(this.certificate != null)
if if(var29 > 0L)
if if(this.address == null)
if if(var126 < var119)
if if(var30 == 0L)
if if(var106 != null)
if if(var3 != null)
if if(var37.handler != null)
if if(var2 == 0L)
if if(var7 < 3)
if if(var4 == null)
if if(var5 != null && var5.handler != null)
if if(var6 == null)
if if(var4 == 0L)
if if(var9 > 0L)
if if(var6 <= 0L)
if if(var3 - this.lastDropLogNanos >= DROP_LOG_INTERVAL_NANOS)
if if(var1 != NetworkChannel.Default && var1 != NetworkChannel.Voice)
private void initChannelvoid initChannel(QuicheChannel var1)
public boolean isActiveboolean isActive()
static boolean isOverMtuSendboolean isOverMtuSend(SocketException var0, long var1)
private void logCloseReasonvoid logCloseReason(Arena var1)
public void processvoid process()
private void returnQueuedBuffersvoid returnQueuedBuffers()
void stopvoid stop()
public void submitvoid submit(InetSocketAddress var1, QuicheListener.DatagramBuffer var2)
public void wakevoid wake()
while while(!this.stopRequested)
while while(true)
while while(true)

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.lib.quiche;

class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.protocol.FormattedMessage;
class="kw">import com.hypixel.hytale.protocol.NetworkChannel;
class="kw">import com.hypixel.hytale.protocol.io.ChannelConnection;
class="kw">import com.hypixel.hytale.protocol.io.ConnectionHandler;
class="kw">import com.hypixel.hytale.protocol.io.ZstdNative;
class="kw">import com.hypixel.hytale.protocol.packets.connection.DisconnectType;
class="kw">import com.hypixel.hytale.protocol.packets.connection.QuicApplicationErrorCode;
class="kw">import com.hypixel.hytale.protocol.packets.connection.ServerDisconnect;
class="kw">import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
class="kw">import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
class="kw">import it.unimi.dsi.fastutil.longs.LongArrayList;
class="kw">import it.unimi.dsi.fastutil.objects.ObjectIterator;
class="kw">import java.io.ByteArrayInputStream;
class="kw">import java.io.IOException;
class="kw">import java.lang.foreign.Arena;
class="kw">import java.lang.foreign.MemorySegment;
class="kw">import java.lang.foreign.ValueLayout;
class="kw">import java.net.InetSocketAddress;
class="kw">import java.net.SocketAddress;
class="kw">import java.net.SocketException;
class="kw">import java.nio.ByteBuffer;
class="kw">import java.nio.channels.ClosedChannelException;
class="kw">import java.nio.charset.StandardCharsets;
class="kw">import java.security.cert.CertificateException;
class="kw">import java.security.cert.CertificateFactory;
class="kw">import java.security.cert.X509Certificate;
class="kw">import java.time.Duration;
class="kw">import java.util.concurrent.ArrayBlockingQueue;
class="kw">import java.util.concurrent.atomic.AtomicBoolean;
class="kw">import java.util.concurrent.locks.LockSupport;
class="kw">import java.util.function.BiFunction;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nullable;

class QuicheConnection {
   class="kw">private class="kw">static class="kw">final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
   class="kw">private class="kw">static class="kw">final int EVENT_QUEUE_SIZE = 1024;
   class="kw">private class="kw">static class="kw">final int INCOMING_QUEUE_SIZE = 64;
   class="kw">private class="kw">static class="kw">final long DROP_LOG_INTERVAL_NANOS = Duration.ofSeconds(1L).toNanos();
   class="kw">private class="kw">static class="kw">final int DATA_BUFFER_SIZE = 65535;
   class="kw">private class="kw">static class="kw">final int QUIC_MIN_DATAGRAM_SIZE = 1200;
   class="kw">public class="kw">static class="kw">final int ASSUMED_CLIENT_MAX_PACKET_SIZE_BYTES = 102400;
   class="kw">public class="kw">final QuicheListener listener;
   class="kw">public class="kw">final byte[] dcid;
   class="kw">private class="kw">final String dcidLabel;
   class="kw">public class="kw">final MemorySegment connHandle;
   class="kw">private class="kw">final ArrayBlockingQueue<QuicheListener.DatagramBuffer> incomingDatagrams = new ArrayBlockingQueue<>(64);
   class="kw">private class="kw">final ArrayBlockingQueue<Runnable> eventTasks = new ArrayBlockingQueue<>(1024);
   class="kw">private int droppedDatagrams;
   class="kw">private long lastDropLogNanos;
   class="kw">private class="kw">final AtomicBoolean active = new AtomicBoolean(true);
   class="kw">private class="kw">volatile boolean stopRequested;
   class="kw">final MemorySegment decompressionContext = ZstdNative.createDCtx();
   class="kw">final MemorySegment compressionContext = ZstdNative.createCCtx();
   class="kw">final Thread processingThread;
   @Nullable
   class="kw">public X509Certificate certificate;
   class="kw">public SocketAddress address;
   class="kw">public String serverName;
   class="kw">private class="kw">final Long2ObjectOpenHashMap<QuicheChannel> streams = new Long2ObjectOpenHashMap();
   class="kw">private class="kw">final LongArrayList writableStreams = new LongArrayList();
   @Nullable
   class="kw">final TokenBucket rateLimitBucket;

   class="kw">public QuicheConnection(QuicheListener var1, byte[] var2, MemorySegment var3) {
      this.listener = var1;
      this.dcid = var2;
      this.dcidLabel = QuicheChannel.formatDcid(var2);
      this.connHandle = var3;
      QuicheConfig.RateLimit var4 = var1.quicheConfig.rateLimit();
      this.rateLimitBucket = var4.isEnabled() ? new TokenBucket(var4.burstCapacity(), var4.packetsPerSecond(), System.nanoTime()) : null;
      this.processingThread = Thread.ofVirtual().name("quiche-conn-" + this.dcidLabel).unstarted(this::process);
   }

   class="kw">public void process() {
      Long2ObjectFunction var1 = var1x -> new QuicheChannel(this, var1x, var1x == 0L ? NetworkChannel.Default : NetworkChannel.Voice);

      try (Arena var2 = Arena.ofConfined()) {
         MemorySegment var105 = var2.allocate(QuicheNative.RECV_INFO_LAYOUT);
         MemorySegment var4 = var2.allocate(QuicheNative.SEND_INFO_LAYOUT);
         MemorySegment var5 = var2.allocate(QuicheNative.SOCKADDR_STORAGE_LAYOUT);
         QuicheNative.setRecvInfoTo(var105, this.listener.localAddr);
         QuicheNative.setRecvInfoToLen(var105, this.listener.localAddrLen);
         MemorySegment var6 = var2.allocate(65535L);
         ByteBuffer var7 = var6.asByteBuffer();
         InetSocketAddress var8 = null;
         MemorySegment var9 = var2.allocate(QuicheNative.SOCKADDR_STORAGE_LAYOUT);
         int var10 = 0;
         MemorySegment var11 = var2.allocate(65535L);
         MemorySegment var12 = var2.allocate(QuicheNative.C_BOOL);
         MemorySegment var13 = var2.allocate(QuicheNative.C_LONG);
         boolean var14 = false;

         while (!this.stopRequested) {
            boolean var15 = false;
            if (QuicheNative.connIsClosed(this.connHandle)) {
               this.logCloseReason(var2);
               break;
            }

            Runnable var16;
            while ((var16 = this.eventTasks.poll()) != null) {
               var15 = true;
               var16.run();
            }

            long var17;
            label1058:
            while ((var17 = QuicheNative.connStreamReadableNext(this.connHandle)) != -1L) {
               QuicheChannel var19 = (QuicheChannel)this.streams.computeIfAbsent(var17, var1);
               if (var19.isNew) {
                  ((HytaleLogger.Api)LOGGER.atInfo()).log("Stream %d created", var17);
                  this.initChannel(var19);
                  var19.isNew = false;
               }

               while (QuicheNative.connStreamReadable(this.connHandle, var17)) {
                  while (true) {
                     long var20 = QuicheNative.connStreamRecv(this.connHandle, var17, var11, var11.byteSize(), var12, var13);
                     if (var20 < 0L) {
                        if (var20 != -1L) {
                           ((HytaleLogger.Api)LOGGER.atSevere())
                              .log("Error reading stream %d: %s (%d)", var17, QuicheNative.QuicheError.fromCode((int)var20), var20);
                        }
                        break;
                     }

                     var15 = true;

                     try {
                        var19.consumePacketData(var11, (int)var20);
                     } catch (Exception var100) {
                        ((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(var100)).log("Exception processing packet on stream %d", var17);
                        this.gracefulClose(var19);
                        break;
                     }

                     long var22 = System.nanoTime();
                     ObjectIterator var24 = this.streams.values().iterator();

                     while (var24.hasNext()) {
                        QuicheChannel var25 = (QuicheChannel)var24.next();
                        var25.checkTimersAndGetDeadline(var22);
                     }

                     if (QuicheNative.connIsClosed(this.connHandle)) {
                        break label1058;
                     }
                  }
               }

               if (QuicheNative.connStreamFinished(this.connHandle, var17)) {
                  ((HytaleLogger.Api)LOGGER.atInfo()).log("Stream %d finished", var17);
                  QuicheChannel var110 = (QuicheChannel)this.streams.remove(var17);
                  if (var110 != null) {
                     var110.close();
                  }
               }
            }

            if (!QuicheNative.connIsClosed(this.connHandle)) {
               while ((var17 = QuicheNative.connStreamWritableNext(this.connHandle)) != -1L) {
                  this.writableStreams.add(var17);
               }

               for (int var108 = this.writableStreams.size() - 1; var108 >= 0; var108--) {
                  QuicheChannel var111 = (QuicheChannel)this.streams.get(this.writableStreams.getLong(var108));
                  if (var111 == null) {
                     this.writableStreams.removeLong(var108);
                  } else {
                     var15 |= var111.tryWrite();
                     if (!var111.isWritable()) {
                        this.writableStreams.removeLong(var108);
                     }
                  }
               }

               QuicheListener.DatagramBuffer var109;
               while ((var109 = this.incomingDatagrams.poll()) != null) {
                  var15 = true;

                  try {
                     int var112 = QuicheUtil.toSocketAddrStorage(var5, var109.source);
                     QuicheNative.setRecvInfoFrom(var105, var5);
                     QuicheNative.setRecvInfoFromLen(var105, var112);
                     long var21 = QuicheNative.connRecv(this.connHandle, var109.memory, var109.buffer.limit(), var105);
                     if (var21 < 0L) {
                        ((HytaleLogger.Api)LOGGER.atWarning()).log("Failed to recv: %s (%d)", QuicheNative.QuicheError.fromCode((int)var21), var21);
                     }
                  } class="kw">finally {
                     this.listener.returnBuffer(var109);
                  }
               }

               while (true) {
                  long var113 = QuicheNative.connSend(this.connHandle, var6, var6.byteSize(), var4);
                  if (var113 < 0L) {
                     if (var113 != -1L) {
                        ((HytaleLogger.Api)LOGGER.atWarning()).log("Failed to send: %s (%d)", QuicheNative.QuicheError.fromCode((int)var113), var113);
                     }
                     break;
                  }

                  if (var113 == 0L) {
                     break;
                  }

                  var15 = true;
                  var7.position(0);
                  var7.limit((int)var113);
                  MemorySegment var117 = QuicheNative.getSendInfoTo(var4);
                  int var23 = QuicheNative.getSendInfoToLen(var4);
                  if (var8 == null || var10 != var23 || MemorySegment.mismatch(var9, 0L, var10, var117, 0L, var23) != -1L) {
                     var9.copyFrom(var117);
                     var10 = var23;
                     var8 = QuicheUtil.fromSocketAddr(var117, var23);
                  }

                  int var121;
                  try {
                     var121 = this.listener.channel.send(var7, var8);
                  } catch (SocketException var99) {
                     if (isOverMtuSend(var99, var113)) {
                        LOGGER.at(Level.FINE).log("Dropping %d-byte datagram the OS refused: %s", var113, var99.getMessage());
                        class="kw">continue;
                     }

                     throw var99;
                  }

                  this.listener.wireBytesSent.addAndGet(var121);
                  if (var121 != var113) {
                     throw new IllegalStateException("Failed to send all bytes");
                  }
               }

               if (QuicheNative.connIsEstablished(this.connHandle) && !var14) {
                  var14 = true;

                  try (Arena var114 = Arena.ofConfined()) {
                     label1099: {
                        MemorySegment var116 = var114.allocate(QuicheNative.C_POINTER);
                        MemorySegment var118 = var114.allocate(QuicheNative.C_SIZE_T);
                        QuicheNative.connPeerCert(this.connHandle, var116, var118);
                        MemorySegment var120 = var116.get(QuicheNative.C_POINTER, 0L);
                        long var122 = QuicheNative.getSizeT(var118, 0L);
                        if (var122 != 0L && !var120.equals(MemorySegment.NULL)) {
                           CertificateFactory var26 = CertificateFactory.getInstance("X.509");
                           this.certificate = (X509Certificate)var26.generateCertificate(
                              new ByteArrayInputStream(var120.reinterpret(var122).toArray(ValueLayout.JAVA_BYTE))
                           );
                           if (this.certificate != null) {
                              ((HytaleLogger.Api)LOGGER.atInfo()).log("Peer certificate retrieved: %s", this.certificate);
                              MemorySegment var27 = var114.allocate(QuicheNative.C_POINTER);
                              MemorySegment var28 = var114.allocate(QuicheNative.C_SIZE_T);
                              QuicheNative.connServerName(this.connHandle, var27, var28);
                              long var29 = QuicheNative.getSizeT(var28, 0L);
                              if (var29 > 0L) {
                                 MemorySegment var31 = var27.get(QuicheNative.C_POINTER, 0L).reinterpret(var29);
                                 this.serverName = new String(var31.toArray(ValueLayout.JAVA_BYTE), StandardCharsets.UTF_8);
                                 ((HytaleLogger.Api)LOGGER.atInfo()).log("SNI hostname: %s", this.serverName);
                              }

                              MemorySegment var129 = var2.allocate(QuicheNative.SOCKADDR_STORAGE_LAYOUT);
                              MemorySegment var32 = var2.allocate(QuicheNative.C_SIZE_T);
                              MemorySegment var33 = QuicheNative.connPathsIter(this.connHandle, this.listener.localAddr, this.listener.localAddrLen);

                              try {
                                 if (QuicheNative.socketAddrIterNext(var33, var129, var32)) {
                                    this.address = QuicheUtil.fromSocketAddr(var129, (int)QuicheNative.getSizeT(var32, 0L));
                                 }
                                 break label1099;
                              } class="kw">finally {
                                 QuicheNative.socketAddrIterFree(var33);
                              }
                           }

                           ((HytaleLogger.Api)LOGGER.atSevere()).log("Missing client cert for %s", this.dcidLabel);
                           QuicheNative.connClose(this.connHandle, true, QuicApplicationErrorCode.AuthFailed.getValue(), MemorySegment.NULL, 0L);
                           class="kw">continue;
                        }

                        ((HytaleLogger.Api)LOGGER.atSevere()).log("Failed to retrieve peer certificate for %s", this.dcidLabel);
                        QuicheNative.connClose(this.connHandle, true, QuicApplicationErrorCode.AuthFailed.getValue(), MemorySegment.NULL, 0L);
                        class="kw">continue;
                     }
                  } catch (CertificateException var98) {
                     ((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(var98)).log("Certificate error for %s", this.dcidLabel);
                     if (!QuicheNative.connIsClosed(this.connHandle)) {
                        QuicheNative.connClose(this.connHandle, true, QuicApplicationErrorCode.AuthFailed.getValue(), MemorySegment.NULL, 0L);
                     }
                     class="kw">continue;
                  }

                  if (this.address == null) {
                     ((HytaleLogger.Api)LOGGER.atSevere()).log("Failed to find connection path for %s", this.dcidLabel);
                     QuicheNative.connClose(this.connHandle, true, QuicApplicationErrorCode.NoError.getValue(), MemorySegment.NULL, 0L);
                     class="kw">continue;
                  }

                  ((HytaleLogger.Api)LOGGER.atInfo()).log("Connection established");
               }

               this.active.set(!QuicheNative.connIsClosed(this.connHandle));
               long var115 = System.nanoTime();
               long var119 = Long.MAX_VALUE;
               ObjectIterator var123 = this.streams.values().iterator();

               while (var123.hasNext()) {
                  QuicheChannel var125 = (QuicheChannel)var123.next();
                  long var126 = var125.checkTimersAndGetDeadline(var115);
                  if (var126 < var119) {
                     var119 = var126;
                  }
               }

               long var124 = QuicheNative.connTimeoutAsNanos(this.connHandle);
               long var127 = var124 < 0L ? Long.MAX_VALUE : var124;
               long var128 = var119 == Long.MAX_VALUE ? Long.MAX_VALUE : Math.max(0L, var119 - var115);
               long var30 = Math.min(var127, var128);
               if (var30 == 0L) {
                  QuicheNative.connOnTimeout(this.connHandle);
               } else if (!var15 && var30 == Long.MAX_VALUE) {
                  LockSupport.park();
               } else if (!var15) {
                  LockSupport.parkNanos(var30);
                  QuicheNative.connOnTimeout(this.connHandle);
               }
            }
         }

         if (!QuicheNative.connIsClosed(this.connHandle)) {
            QuicheChannel var106 = (QuicheChannel)this.streams.get(0L);
            if (var106 != null) {
               this.gracefulClose(var106);
            } else {
               QuicheNative.connClose(this.connHandle, true, QuicApplicationErrorCode.NoError.getValue(), MemorySegment.NULL, 0L);
            }

            this.drainSendQueue();
         }
      } catch (ClosedChannelException var102) {
         ((HytaleLogger.Api)LOGGER.atInfo()).log("Connection %s shutting down", this.dcidLabel);
      } catch (Exception var103) {
         ((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(var103)).log("Unhandled exception in connection %s processing", this.dcidLabel);

         try {
            QuicheChannel var3 = (QuicheChannel)this.streams.get(0L);
            if (var3 != null) {
               this.gracefulClose(var3);
            } else if (!QuicheNative.connIsClosed(this.connHandle)) {
               QuicheNative.connClose(this.connHandle, false, 1L, MemorySegment.NULL, 0L);
            }

            this.drainSendQueue();
         } catch (Exception var92) {
            ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var92)).log("Error during connection close cleanup for %s", this.dcidLabel);
         }
      } class="kw">finally {
         ObjectIterator var36 = this.streams.values().iterator();

         while (var36.hasNext()) {
            QuicheChannel var37 = (QuicheChannel)var36.next();

            try {
               if (var37.handler != null) {
                  var37.handler.logCloseMessage();
               }
            } catch (Exception var91) {
               ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var91)).log("Error logging close message for stream %d", var37.getStreamId());
            }

            try {
               var37.close();
            } catch (Exception var90) {
               ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var90)).log("Error closing stream %d", var37.getStreamId());
            }
         }

         this.active.set(false);
         this.returnQueuedBuffers();
         QuicheNative.connFree(this.connHandle);
         ZstdNative.freeDCtx(this.decompressionContext);
         ZstdNative.freeCCtx(this.compressionContext);
         this.listener.liveConnections.remove(this);
      }
   }

   class="kw">private void initChannel(QuicheChannel var1) {
      long var2 = var1.getStreamId();
      if (var2 == 0L) {
         ((HytaleLogger.Api)LOGGER.atInfo()).log("Primary stream %d created", var2);
         int var7 = this.getNegotiatedAlpnVersion();
         if (var7 < 3) {
            ((HytaleLogger.Api)LOGGER.atInfo()).log("Rejecting connection %s: ALPN version %d < required %d", this.dcidLabel, var7, 3);
            FormattedMessage var8 = new FormattedMessage();
            var8.messageId = "client.general.disconnect.clientOutdated";
            var1.handler = this.listener.handler.apply(var1);
            var1.handler.registered(null);
            var1.writeForClose(new ServerDisconnect(var8, DisconnectType.Disconnect));
            QuicheChannel.closeWithReason(this.connHandle, QuicApplicationErrorCode.ClientOutdated, var8);
         } else {
            QuicheNative.connStreamPriority(this.connHandle, var2, (byte)0, false);
            var1.setPacketTimeout(this.listener.quicheConfig.initialPacketTimeout());
            var1.handler = this.listener.handler.apply(var1);
            var1.handler.registered(null);
         }
      } else if ((var2 & 3L) == 0L) {
         BiFunction var4 = this.listener.auxStreamHandler;
         if (var4 == null) {
            ((HytaleLogger.Api)LOGGER.atWarning()).log("No aux stream handler configured, closing stream %d", var2);
            QuicheNative.connStreamShutdown(this.connHandle, var2, 0, 0L);
            QuicheNative.connStreamShutdown(this.connHandle, var2, 1, 0L);
         } else {
            QuicheChannel var5 = (QuicheChannel)this.streams.get(0L);
            if (var5 != null && var5.handler != null) {
               ConnectionHandler var6 = var4.apply(var5.handler, var1);
               if (var6 == null) {
                  ((HytaleLogger.Api)LOGGER.atWarning()).log("Aux stream %d rejected by handler", var2);
                  QuicheNative.connStreamShutdown(this.connHandle, var2, 0, 0L);
                  QuicheNative.connStreamShutdown(this.connHandle, var2, 1, 0L);
               } else {
                  var1.setPacketTimeout(this.listener.quicheConfig.auxStreamPendingTimeout());
                  var1.handler = var6;
                  var6.registered(null);
               }
            } else {
               ((HytaleLogger.Api)LOGGER.atWarning()).log("Primary stream not ready, closing aux stream %d", var2);
               QuicheNative.connStreamShutdown(this.connHandle, var2, 0, 0L);
               QuicheNative.connStreamShutdown(this.connHandle, var2, 1, 0L);
            }
         }
      } else {
         ((HytaleLogger.Api)LOGGER.atWarning()).log("Unexpected stream type for stream %d (low bits: %d), ignoring", var2, var2 & 3L);
      }
   }

   class="kw">public boolean isActive() {
      class="kw">return this.active.get();
   }

   class="kw">private int getNegotiatedAlpnVersion() {
      try (Arena var1 = Arena.ofConfined()) {
         MemorySegment var2 = var1.allocate(QuicheNative.C_POINTER);
         MemorySegment var3 = var1.allocate(QuicheNative.C_SIZE_T);
         QuicheNative.connApplicationProto(this.connHandle, var2, var3);
         long var4 = QuicheNative.getSizeT(var3, 0L);
         if (var4 == 0L) {
            class="kw">return 0;
         }

         MemorySegment var6 = var2.get(QuicheNative.C_POINTER, 0L).reinterpret(var4);
         String var7 = new String(var6.toArray(ValueLayout.JAVA_BYTE), StandardCharsets.UTF_8);
         if (var7.startsWith("hytale/")) {
            try {
               class="kw">return Integer.parseInt(var7.substring(7));
            } catch (NumberFormatException var11) {
               class="kw">return 0;
            }
         } else {
            class="kw">return 0;
         }
      }
   }

   class="kw">public void execute(Runnable var1) {
      if (!this.eventTasks.offer(var1)) {
         ((HytaleLogger.Api)LOGGER.atSevere()).log("Event task queue full for %s, terminating connection", this.dcidLabel);
         this.stop();
      } else {
         LockSupport.unpark(this.processingThread);
      }
   }

   void stop() {
      this.stopRequested = true;
      this.wake();
   }

   class="kw">public void wake() {
      LockSupport.unpark(this.processingThread);
   }

   class="kw">private void logCloseReason(Arena var1) {
      if (QuicheNative.connIsTimedOut(this.connHandle)) {
         ((HytaleLogger.Api)LOGGER.atInfo()).log("Connection %s closed: timed out", this.dcidLabel);
      } else {
         MemorySegment var2 = var1.allocate(QuicheNative.C_BOOL);
         MemorySegment var3 = var1.allocate(QuicheNative.C_LONG);
         MemorySegment var4 = var1.allocate(QuicheNative.C_POINTER);
         MemorySegment var5 = var1.allocate(QuicheNative.C_SIZE_T);
         if (QuicheNative.connPeerError(this.connHandle, var2, var3, var4, var5)) {
            long var14 = var3.get(ValueLayout.JAVA_LONG, 0L);
            boolean var15 = var2.get(ValueLayout.JAVA_BOOLEAN, 0L);
            long var9 = QuicheNative.getSizeT(var5, 0L);
            String var11 = "";
            if (var9 > 0L) {
               MemorySegment var12 = var4.get(QuicheNative.C_POINTER, 0L);
               if (var12.address() != 0L) {
                  MemorySegment var13 = var12.reinterpret(var9);
                  var11 = " reason=\"" + new String(var13.toArray(ValueLayout.JAVA_BYTE), StandardCharsets.UTF_8) + "\"";
               }
            }

            ((HytaleLogger.Api)LOGGER.atInfo())
               .log("Connection %s closed by peer: %s error code %d%s", this.dcidLabel, var15 ? "application" : "transport", var14, var11);
         } else if (QuicheNative.connLocalError(this.connHandle, var2, var3, var4, var5)) {
            long var6 = var3.get(ValueLayout.JAVA_LONG, 0L);
            boolean var8 = var2.get(ValueLayout.JAVA_BOOLEAN, 0L);
            ((HytaleLogger.Api)LOGGER.atInfo()).log("Connection %s closed locally: %s error code %d", this.dcidLabel, var8 ? "application" : "transport", var6);
         } else {
            ((HytaleLogger.Api)LOGGER.atInfo()).log("Connection %s closed (reason unknown)", this.dcidLabel);
         }
      }
   }

   class="kw">private void gracefulClose(QuicheChannel var1) {
      if (!QuicheNative.connIsClosed(this.connHandle)) {
         FormattedMessage var2 = new FormattedMessage();
         var2.messageId = "client.general.disconnect.internalServerError";
         var1.writeForClose(new ServerDisconnect(var2, DisconnectType.Crash));
         QuicheChannel.closeWithReason(this.connHandle, QuicApplicationErrorCode.Crash, var2);
      }
   }

   class="kw">private class="kw">static boolean isOverMtuSend(SocketException var0, long var1) {
      class="kw">return var0.getClass() != SocketException.class ? false : var1 > 1200L;
   }

   class="kw">private void drainSendQueue() {
      try (Arena var1 = Arena.ofConfined()) {
         MemorySegment var2 = var1.allocate(65535L);
         ByteBuffer var3 = var2.asByteBuffer();
         MemorySegment var4 = var1.allocate(QuicheNative.SEND_INFO_LAYOUT);

         for (int var5 = 0; var5 < 100; var5++) {
            long var6 = QuicheNative.connSend(this.connHandle, var2, var2.byteSize(), var4);
            if (var6 <= 0L) {
               break;
            }

            var3.position(0);
            var3.limit((int)var6);
            InetSocketAddress var8 = QuicheUtil.fromSocketAddr(QuicheNative.getSendInfoTo(var4), QuicheNative.getSendInfoToLen(var4));

            try {
               this.listener.channel.send(var3, var8);
            } catch (SocketException var11) {
               if (!isOverMtuSend(var11, var6)) {
                  throw var11;
               }
            }
         }
      } catch (IOException var13) {
         ((HytaleLogger.Api)LOGGER.atWarning()).log("Failed to drain send queue for %s: %s", this.dcidLabel, var13.getMessage());
      }
   }

   class="kw">public void submit(InetSocketAddress var1, QuicheListener.DatagramBuffer var2) {
      var2.source = var1;
      if (!this.incomingDatagrams.offer(var2)) {
         this.listener.returnBuffer(var2);
         this.droppedDatagrams++;
         long var3 = System.nanoTime();
         if (var3 - this.lastDropLogNanos >= DROP_LOG_INTERVAL_NANOS) {
            this.lastDropLogNanos = var3;
            ((HytaleLogger.Api)LOGGER.atInfo()).log("%s: isn't processing packets fast enough, discarded %d datagrams", this.dcidLabel, this.droppedDatagrams);
         }
      } else if (!this.active.get()) {
         this.returnQueuedBuffers();
      } else {
         LockSupport.unpark(this.processingThread);
      }
   }

   class="kw">private void returnQueuedBuffers() {
      QuicheListener.DatagramBuffer var1;
      while ((var1 = this.incomingDatagrams.poll()) != null) {
         this.listener.returnBuffer(var1);
      }
   }

   QuicheChannel createChannel(NetworkChannel var1) {
      if (var1 != NetworkChannel.Default && var1 != NetworkChannel.Voice) {
         int var2 = var1.ordinal() - 1 << 2 | 3;
         QuicheChannel var3 = (QuicheChannel)this.streams.computeIfAbsent(var2, var2x -> new QuicheChannel(this, var2x, var1));
         this.writableStreams.add(var2);
         class="kw">return var3;
      } else {
         throw new IllegalArgumentException("Cannot create server-initiated uni stream for " + var1);
      }
   }
}