@Override
    public void run() {
      try {
        // Authenticate
        URLConnection connection = new URL(this.postURL).openConnection();

        JsonObject json;
        try (InputStream is = connection.getInputStream()) {
          if (is.available() == 0) {
            this.session.disconnect("Invalid username or session id!");
            return;
          }
          try {
            json = gson.fromJson(new InputStreamReader(is), JsonObject.class);
          } catch (Exception e) {
            LanternGame.log().warn("Username \"" + this.username + "\" failed to authenticate!");
            this.session.disconnect("Failed to verify username!");
            return;
          }
        }

        String name = json.get("name").getAsString();
        String id = json.get("id").getAsString();

        // Parse UUID
        UUID uuid;

        try {
          uuid = UUIDHelper.fromFlatString(id);
        } catch (IllegalArgumentException e) {
          LanternGame.log().error("Returned authentication UUID invalid: " + id, e);
          this.session.disconnect("Invalid UUID.");
          return;
        }

        JsonArray propsArray = json.getAsJsonArray("properties");

        // Parse properties
        Multimap<String, ProfileProperty> properties = LinkedHashMultimap.create();
        for (JsonElement element : propsArray) {
          JsonObject json0 = element.getAsJsonObject();
          String propName = json0.get("name").getAsString();
          String value = json0.get("value").getAsString();
          String signature = json0.has("signature") ? json0.get("signature").getAsString() : null;
          properties.put(propName, new LanternProfileProperty(propName, value, signature));
        }

        LanternGame.log().info("Finished authenticating.");
        this.session.messageReceived(
            new MessageLoginInFinish(new LanternGameProfile(uuid, name, properties)));
      } catch (Exception e) {
        LanternGame.log().error("Error in authentication thread", e);
        this.session.disconnect("Internal error during authentication.", true);
      }
    }
 LanternWallType(String identifier, String translationPart) {
   this.translation =
       LanternGame.get()
           .getRegistry()
           .getTranslationManager()
           .get("tile.cobbleWall." + translationPart + ".name");
   this.identifier = identifier;
 }
 @Override
 public void handle(NetworkContext context, MessageForgeHandshakeInOutAck message) {
   Session session = context.getSession();
   Attribute<ForgeServerHandshakePhase> phase =
       context.getChannel().attr(ForgeHandshakePhase.PHASE);
   switch (phase.get()) {
     case WAITING_ACK:
       if (!message.getPhase().equals(ForgeClientHandshakePhase.WAITING_SERVER_DATA)) {
         session.disconnect(
             "Retrieved unexpected forge handshake ack message. (Got "
                 + message.getPhase()
                 + ", expected "
                 + ForgeClientHandshakePhase.WAITING_SERVER_DATA
                 + ")");
       } else {
         List<MessageForgeHandshakeOutRegistryData.Entry> entries = Lists.newArrayList();
         entries.add(
             new MessageForgeHandshakeOutRegistryData.Entry(
                 "fml:items", Maps.newHashMap(), Lists.newArrayList()));
         entries.add(
             new MessageForgeHandshakeOutRegistryData.Entry(
                 "fml:blocks", Maps.newHashMap(), Lists.newArrayList()));
         session.send(new MessageForgeHandshakeOutRegistryData(entries));
         session.send(new MessageForgeHandshakeInOutAck(ForgeServerHandshakePhase.WAITING_ACK));
         phase.set(ForgeServerHandshakePhase.COMPLETE);
       }
       LanternGame.log()
           .info(
               "{}: Forge handshake -> Received ack (waitingServerData) message.",
               session.getGameProfile().getName());
       break;
     case COMPLETE:
       if (!message.getPhase().equals(ForgeClientHandshakePhase.WAITING_SERVER_COMPLETE)) {
         session.disconnect(
             "Retrieved unexpected forge handshake ack message. (Got "
                 + message.getPhase()
                 + ", expected "
                 + ForgeClientHandshakePhase.WAITING_SERVER_COMPLETE
                 + ")");
       } else {
         session.send(new MessageForgeHandshakeInOutAck(ForgeServerHandshakePhase.COMPLETE));
         phase.set(ForgeServerHandshakePhase.DONE);
       }
       LanternGame.log()
           .info(
               "{}: Forge handshake -> Received ack (waitingServerComplete) message.",
               session.getGameProfile().getName());
       break;
     case DONE:
       if (!message.getPhase().equals(ForgeClientHandshakePhase.PENDING_COMPLETE)
           && !message.getPhase().equals(ForgeClientHandshakePhase.COMPLETE)) {
         session.disconnect(
             "Retrieved unexpected forge handshake ack message. (Got "
                 + message.getPhase()
                 + ", expected "
                 + ForgeClientHandshakePhase.PENDING_COMPLETE
                 + " or "
                 + ForgeClientHandshakePhase.COMPLETE
                 + ")");
       } else {
         if (message.getPhase().equals(ForgeClientHandshakePhase.PENDING_COMPLETE)) {
           session.send(new MessageForgeHandshakeInOutAck(ForgeServerHandshakePhase.DONE));
           LanternGame.log()
               .info(
                   "{}: Forge handshake -> Received ack (pendingComplete) message.",
                   session.getGameProfile().getName());
         } else {
           session.setProtocolState(ProtocolState.PLAY);
           session.spawnPlayer();
           LanternGame.log()
               .info(
                   "{}: Forge handshake -> Received ack (complete) message.",
                   session.getGameProfile().getName());
         }
       }
       break;
     case ERROR:
       break;
     default:
       session.disconnect(
           "Retrieved unexpected forge handshake ack message. (Got " + message.getPhase() + ")");
   }
 }
  @Override
  public void handle(NetworkContext context, MessageLoginInEncryptionResponse message) {
    Session session = context.getSession();
    PrivateKey privateKey = session.getServer().getKeyPair().getPrivate();

    // Create rsaCipher
    Cipher rsaCipher;
    try {
      rsaCipher = Cipher.getInstance("RSA");
    } catch (GeneralSecurityException e) {
      LanternGame.log().error("Could not initialize RSA cipher", e);
      session.disconnect("Unable to initialize RSA cipher.");
      return;
    }

    // Decrypt shared secret
    SecretKey sharedSecret;
    try {
      rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
      sharedSecret = new SecretKeySpec(rsaCipher.doFinal(message.getSharedSecret()), "AES");
    } catch (Exception e) {
      LanternGame.log().warn("Could not decrypt shared secret", e);
      session.disconnect("Unable to decrypt shared secret.");
      return;
    }

    // Decrypt verify token
    byte[] verifyToken;
    try {
      rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
      verifyToken = rsaCipher.doFinal(message.getVerifyToken());
    } catch (Exception e) {
      LanternGame.log().warn("Could not decrypt verify token", e);
      session.disconnect("Unable to decrypt verify token.");
      return;
    }

    // Check verify token
    if (!Arrays.equals(verifyToken, session.getVerifyToken())) {
      session.disconnect("Invalid verify token.");
      return;
    }

    // Initialize stream encryption
    session.setEncryption(sharedSecret);

    // Create hash for auth
    String hash;
    try {
      MessageDigest digest = MessageDigest.getInstance("SHA-1");
      String sessionId = context.getChannel().attr(HandlerLoginStart.SESSION_ID).getAndRemove();

      digest.update(sessionId.getBytes());
      digest.update(sharedSecret.getEncoded());
      digest.update(session.getServer().getKeyPair().getPublic().getEncoded());

      // BigInteger takes care of sign and leading zeroes
      hash = new BigInteger(digest.digest()).toString(16);
    } catch (NoSuchAlgorithmException e) {
      LanternGame.log().error("Unable to generate SHA-1 digest", e);
      session.disconnect("Failed to hash login data.");
      return;
    }

    // Start auth thread
    Thread clientAuthThread =
        new Thread(new ClientAuthRunnable(session, session.getVerifyUsername(), hash));
    clientAuthThread.setName("ClientAuth{" + session.getVerifyUsername() + "}");
    clientAuthThread.start();
  }