@Override
    public void run() {
      boolean verify =
          this.session.hasFlag(MinecraftConstants.VERIFY_USERS_KEY)
              ? this.session.<Boolean>getFlag(MinecraftConstants.VERIFY_USERS_KEY)
              : true;

      GameProfile profile = null;
      if (verify && this.key != null) {
        Proxy proxy = this.session.<Proxy>getFlag(MinecraftConstants.AUTH_PROXY_KEY);
        if (proxy == null) {
          proxy = Proxy.NO_PROXY;
        }

        try {
          profile =
              new SessionService(proxy)
                  .getProfileByServer(
                      username,
                      new BigInteger(
                              CryptUtil.getServerIdHash(serverId, KEY_PAIR.getPublic(), this.key))
                          .toString(16));
        } catch (RequestException e) {
          this.session.disconnect("Failed to make session service request.", e);
          return;
        }

        if (profile == null) {
          this.session.disconnect("Failed to verify username.");
        }
      } else {
        profile =
            new GameProfile(
                UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes()), username);
      }

      int threshold;
      if (this.session.hasFlag(MinecraftConstants.SERVER_COMPRESSION_THRESHOLD)) {
        threshold = this.session.getFlag(MinecraftConstants.SERVER_COMPRESSION_THRESHOLD);
      } else {
        threshold = 256;
      }

      this.session.send(new LoginSetCompressionPacket(threshold));
      this.session.setCompressionThreshold(threshold);
      this.session.send(new LoginSuccessPacket(profile));
      this.session.setFlag(MinecraftConstants.PROFILE_KEY, profile);
      ((MinecraftProtocol) this.session.getPacketProtocol())
          .setSubProtocol(SubProtocol.GAME, false, this.session);
      ServerLoginHandler handler =
          this.session.getFlag(MinecraftConstants.SERVER_LOGIN_HANDLER_KEY);
      if (handler != null) {
        handler.loggedIn(this.session);
      }

      new Thread(new KeepAliveTask(this.session)).start();
    }
 @Override
 public void read(NetInput in) throws IOException {
   this.serverId = in.readString();
   this.publicKey = CryptUtil.decodePublicKey(in.readBytes(in.readVarInt()));
   this.verifyToken = in.readBytes(in.readVarInt());
 }
public class ServerListener extends SessionAdapter {
  private static final KeyPair KEY_PAIR = CryptUtil.generateKeyPair();

  private byte verifyToken[] = new byte[4];
  private String serverId = "";
  private String username = "";

  private long lastPingTime = 0;
  private int lastPingId = 0;

  public ServerListener() {
    new Random().nextBytes(this.verifyToken);
  }

  @Override
  public void connected(ConnectedEvent event) {
    event.getSession().setFlag(MinecraftConstants.PING_KEY, 0);
  }

  @Override
  public void packetReceived(PacketReceivedEvent event) {
    MinecraftProtocol protocol = (MinecraftProtocol) event.getSession().getPacketProtocol();
    if (protocol.getSubProtocol() == SubProtocol.HANDSHAKE) {
      if (event.getPacket() instanceof HandshakePacket) {
        HandshakePacket packet = event.getPacket();
        switch (packet.getIntent()) {
          case STATUS:
            protocol.setSubProtocol(SubProtocol.STATUS, false, event.getSession());
            break;
          case LOGIN:
            protocol.setSubProtocol(SubProtocol.LOGIN, false, event.getSession());
            if (packet.getProtocolVersion() > MinecraftConstants.PROTOCOL_VERSION) {
              event
                  .getSession()
                  .disconnect(
                      "Outdated server! I'm still on " + MinecraftConstants.GAME_VERSION + ".");
            } else if (packet.getProtocolVersion() < MinecraftConstants.PROTOCOL_VERSION) {
              event
                  .getSession()
                  .disconnect(
                      "Outdated client! Please use " + MinecraftConstants.GAME_VERSION + ".");
            }

            break;
          default:
            throw new UnsupportedOperationException("Invalid client intent: " + packet.getIntent());
        }
      }
    }

    if (protocol.getSubProtocol() == SubProtocol.LOGIN) {
      if (event.getPacket() instanceof LoginStartPacket) {
        this.username = event.<LoginStartPacket>getPacket().getUsername();

        boolean verify =
            event.getSession().hasFlag(MinecraftConstants.VERIFY_USERS_KEY)
                ? event.getSession().<Boolean>getFlag(MinecraftConstants.VERIFY_USERS_KEY)
                : true;
        if (verify) {
          event
              .getSession()
              .send(
                  new EncryptionRequestPacket(
                      this.serverId, KEY_PAIR.getPublic(), this.verifyToken));
        } else {
          new Thread(new UserAuthTask(event.getSession(), null)).start();
        }
      } else if (event.getPacket() instanceof EncryptionResponsePacket) {
        EncryptionResponsePacket packet = event.getPacket();
        PrivateKey privateKey = KEY_PAIR.getPrivate();
        if (!Arrays.equals(this.verifyToken, packet.getVerifyToken(privateKey))) {
          event.getSession().disconnect("Invalid nonce!");
          return;
        }

        SecretKey key = packet.getSecretKey(privateKey);
        protocol.enableEncryption(key);
        new Thread(new UserAuthTask(event.getSession(), key)).start();
      }
    }

    if (protocol.getSubProtocol() == SubProtocol.STATUS) {
      if (event.getPacket() instanceof StatusQueryPacket) {
        ServerInfoBuilder builder =
            event.getSession().getFlag(MinecraftConstants.SERVER_INFO_BUILDER_KEY);
        if (builder == null) {
          builder =
              new ServerInfoBuilder() {
                @Override
                public ServerStatusInfo buildInfo(Session session) {
                  return new ServerStatusInfo(
                      VersionInfo.CURRENT,
                      new PlayerInfo(0, 20, new GameProfile[] {}),
                      Message.fromString("A Minecraft Server"),
                      null);
                }
              };
        }

        ServerStatusInfo info = builder.buildInfo(event.getSession());
        event.getSession().send(new StatusResponsePacket(info));
      } else if (event.getPacket() instanceof StatusPingPacket) {
        event
            .getSession()
            .send(new StatusPongPacket(event.<StatusPingPacket>getPacket().getPingTime()));
      }
    }

    if (protocol.getSubProtocol() == SubProtocol.GAME) {
      if (event.getPacket() instanceof ClientKeepAlivePacket) {
        ClientKeepAlivePacket packet = event.getPacket();
        if (packet.getPingId() == this.lastPingId) {
          long time = System.currentTimeMillis() - this.lastPingTime;
          event.getSession().setFlag(MinecraftConstants.PING_KEY, time);
        }
      }
    }
  }

  @Override
  public void disconnecting(DisconnectingEvent event) {
    MinecraftProtocol protocol = (MinecraftProtocol) event.getSession().getPacketProtocol();
    if (protocol.getSubProtocol() == SubProtocol.LOGIN) {
      event.getSession().send(new LoginDisconnectPacket(event.getReason()));
    } else if (protocol.getSubProtocol() == SubProtocol.GAME) {
      event.getSession().send(new ServerDisconnectPacket(event.getReason()));
    }
  }

  private class UserAuthTask implements Runnable {
    private Session session;
    private SecretKey key;

    public UserAuthTask(Session session, SecretKey key) {
      this.key = key;
      this.session = session;
    }

    @Override
    public void run() {
      boolean verify =
          this.session.hasFlag(MinecraftConstants.VERIFY_USERS_KEY)
              ? this.session.<Boolean>getFlag(MinecraftConstants.VERIFY_USERS_KEY)
              : true;

      GameProfile profile = null;
      if (verify && this.key != null) {
        Proxy proxy = this.session.<Proxy>getFlag(MinecraftConstants.AUTH_PROXY_KEY);
        if (proxy == null) {
          proxy = Proxy.NO_PROXY;
        }

        try {
          profile =
              new SessionService(proxy)
                  .getProfileByServer(
                      username,
                      new BigInteger(
                              CryptUtil.getServerIdHash(serverId, KEY_PAIR.getPublic(), this.key))
                          .toString(16));
        } catch (RequestException e) {
          this.session.disconnect("Failed to make session service request.", e);
          return;
        }

        if (profile == null) {
          this.session.disconnect("Failed to verify username.");
        }
      } else {
        profile =
            new GameProfile(
                UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes()), username);
      }

      int threshold;
      if (this.session.hasFlag(MinecraftConstants.SERVER_COMPRESSION_THRESHOLD)) {
        threshold = this.session.getFlag(MinecraftConstants.SERVER_COMPRESSION_THRESHOLD);
      } else {
        threshold = 256;
      }

      this.session.send(new LoginSetCompressionPacket(threshold));
      this.session.setCompressionThreshold(threshold);
      this.session.send(new LoginSuccessPacket(profile));
      this.session.setFlag(MinecraftConstants.PROFILE_KEY, profile);
      ((MinecraftProtocol) this.session.getPacketProtocol())
          .setSubProtocol(SubProtocol.GAME, false, this.session);
      ServerLoginHandler handler =
          this.session.getFlag(MinecraftConstants.SERVER_LOGIN_HANDLER_KEY);
      if (handler != null) {
        handler.loggedIn(this.session);
      }

      new Thread(new KeepAliveTask(this.session)).start();
    }
  }

  private class KeepAliveTask implements Runnable {
    private Session session;

    public KeepAliveTask(Session session) {
      this.session = session;
    }

    @Override
    public void run() {
      while (this.session.isConnected()) {
        lastPingTime = System.currentTimeMillis();
        lastPingId = (int) lastPingTime;
        this.session.send(new ServerKeepAlivePacket(lastPingId));

        try {
          Thread.sleep(2000);
        } catch (InterruptedException e) {
          break;
        }
      }
    }
  }
}