private static void printHelp(OptionParser parser) {
   try {
     parser.printHelpOn(System.out);
   } catch (Exception exception) {
     exception.printStackTrace();
   }
 }
  @EventHandler
  public void onTick(TickEvent event) {
    if (!bot.hasSpawned() || !bot.isConnected()) return;
    if (tickDelay > 0) {
      tickDelay--;
      return;
    }

    MainPlayerEntity player = bot.getPlayer();
    if (player == null || !bot.hasSpawned() || spamMessage == null) return;
    if (nextMessage > 0) {
      nextMessage--;
      return;
    }
    try {
      String message = spamMessage;
      MessageFormatter formatter = new MessageFormatter();
      synchronized (bots) {
        if (bots.size() > 0) {
          DarkBotMCSpambot bot = bots.get(++nextBot >= bots.size() ? nextBot = 0 : nextBot);
          if (bot != null && bot.bot != null && bot.bot.getSession() != null)
            formatter.setVariable("bot", bot.bot.getSession().getUsername());
        }
      }
      if (spamList.length > 0) {
        formatter.setVariable(
            "spamlist",
            spamList[++nextSpamList >= spamList.length ? nextSpamList = 0 : nextSpamList]);
      }
      formatter.setVariable("rnd", Util.generateRandomString(15 + random.nextInt(6)));
      formatter.setVariable(
          "msg",
          Character.toString(
              msgChars[++nextMsgChar >= msgChars.length ? nextMsgChar = 0 : nextMsgChar]));
      message = formatter.format(message);
      bot.say(message);
    } catch (Exception e) {
      e.printStackTrace();
    }
    nextMessage = messageDelay;
  }
 private static void initGui() {
   try {
     UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
   } catch (Exception exception) {
     exception.printStackTrace();
   }
   JFrame frame = new JFrame("DarkBot");
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setLayout(new GridBagLayout());
   Insets noInsets = new Insets(0, 0, 0, 0);
   final JToggleButton sessionsButton = new JToggleButton("Login (0)");
   frame.add(
       sessionsButton,
       new GridBagConstraints(
           0, 0, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, noInsets, 0, 0));
   sessionsButton.addActionListener(
       new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
           sessions.set(sessionsButton.isSelected());
           synchronized (sessions) {
             sessions.notifyAll();
           }
         }
       });
   final JToggleButton joinsButton = new JToggleButton("Join (0)");
   frame.add(
       joinsButton,
       new GridBagConstraints(
           0, 1, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, noInsets, 0, 0));
   joinsButton.addActionListener(
       new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
           joins.set(joinsButton.isSelected());
           synchronized (joins) {
             joins.notifyAll();
           }
         }
       });
   final JTextField field = new JTextField();
   frame.add(
       field,
       new GridBagConstraints(
           0, 2, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, noInsets, 0, 0));
   final JButton button = new JButton("Start");
   frame.add(
       button,
       new GridBagConstraints(
           1, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, noInsets, 0, 0));
   button.addActionListener(
       new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
           if (button.getText().startsWith("Start")) {
             field.setEnabled(false);
             spamMessage = field.getText();
             button.setText("Stop");
           } else {
             spamMessage = null;
             button.setText("Start");
             field.setEnabled(true);
           }
         }
       });
   Timer timer =
       new Timer(
           500,
           new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent e) {
               sessionsButton.setText(
                   sessionsButton.getText().split(" ")[0]
                       + " ("
                       + Integer.toString(sessionCount.get())
                       + ")");
               joinsButton.setText(
                   joinsButton.getText().split(" ")[0]
                       + " ("
                       + Integer.toString(amountJoined.get())
                       + ")");
             }
           });
   timer.setRepeats(true);
   timer.start();
   frame.pack();
   frame.setSize(500, frame.getHeight());
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }
  public static void main(String[] args) {
    // TODO main
    OptionParser parser = new OptionParser();
    parser.acceptsAll(Arrays.asList("h", "help"), "Show this help dialog.");
    OptionSpec<String> serverOption =
        parser
            .acceptsAll(Arrays.asList("s", "server"), "Server to join.")
            .withRequiredArg()
            .describedAs("server-address[:port]");
    OptionSpec<String> proxyOption =
        parser
            .acceptsAll(
                Arrays.asList("P", "proxy"),
                "SOCKS proxy to use. Ignored in presence of 'socks-proxy-list'.")
            .withRequiredArg()
            .describedAs("proxy-address");
    OptionSpec<String> ownerOption =
        parser
            .acceptsAll(
                Arrays.asList("o", "owner"), "Owner of the bot (username of in-game control).")
            .withRequiredArg()
            .describedAs("username");
    OptionSpec<?> offlineOption =
        parser.acceptsAll(
            Arrays.asList("O", "offline"),
            "Offline-mode. Ignores 'password' and 'account-list' (will "
                + "generate random usernames if 'username' is not supplied).");
    OptionSpec<?> autoRejoinOption =
        parser.acceptsAll(Arrays.asList("a", "auto-rejoin"), "Auto-rejoin a server on disconnect.");
    OptionSpec<Integer> loginDelayOption =
        parser
            .acceptsAll(
                Arrays.asList("d", "login-delay"),
                "Delay between bot joins, in milliseconds. 5000 is "
                    + "recommended if not using socks proxies.")
            .withRequiredArg()
            .describedAs("delay")
            .ofType(Integer.class);
    OptionSpec<Integer> botAmountOption =
        parser
            .acceptsAll(
                Arrays.asList("b", "bot-amount"),
                "Amount of bots to join. Must be <= amount of accounts.")
            .withRequiredArg()
            .describedAs("amount")
            .ofType(Integer.class);

    OptionSpec<String> accountListOption =
        parser
            .accepts(
                "account-list",
                "File containing a list of accounts, in username/email:password format.")
            .withRequiredArg()
            .describedAs("file");
    OptionSpec<String> socksProxyListOption =
        parser
            .accepts(
                "socks-proxy-list",
                "File containing a list of SOCKS proxies, in address:port format.")
            .withRequiredArg()
            .describedAs("file");
    OptionSpec<String> httpProxyListOption =
        parser
            .accepts(
                "http-proxy-list",
                "File containing a list of HTTP proxies, in address:port format.")
            .withRequiredArg()
            .describedAs("file");

    OptionSet options;
    try {
      options = parser.parse(args);
    } catch (OptionException exception) {
      try {
        parser.printHelpOn(System.out);
      } catch (Exception exception1) {
        exception1.printStackTrace();
      }
      return;
    }

    if (options.has("help")) {
      printHelp(parser);
      return;
    }

    final boolean offline = options.has(offlineOption);
    final boolean autoRejoin = options.has(autoRejoinOption);

    final List<String> accounts;
    if (options.has(accountListOption)) {
      accounts = loadAccounts(options.valueOf(accountListOption));
    } else if (!offline) {
      System.out.println("Option 'accounts' must be supplied in " + "absence of option 'offline'.");
      printHelp(parser);
      return;
    } else accounts = null;

    final String server;
    if (!options.has(serverOption)) {
      System.out.println("Option 'server' required.");
      printHelp(parser);
      return;
    } else server = options.valueOf(serverOption);

    final String owner;
    if (!options.has(ownerOption)) {
      System.out.println("Option 'owner' required.");
      printHelp(parser);
      return;
    } else owner = options.valueOf(ownerOption);

    final List<String> socksProxies;
    if (options.has(socksProxyListOption))
      socksProxies = loadProxies(options.valueOf(socksProxyListOption));
    else socksProxies = null;
    final boolean useProxy = socksProxies != null;

    final List<String> httpProxies;
    if (options.has(httpProxyListOption))
      httpProxies = loadLoginProxies(options.valueOf(httpProxyListOption));
    else if (!offline && accounts != null) {
      System.out.println(
          "Option 'http-proxy-list' required if " + "option 'account-list' is supplied.");
      printHelp(parser);
      return;
    } else httpProxies = null;

    final int loginDelay;
    if (options.has(loginDelayOption)) loginDelay = options.valueOf(loginDelayOption);
    else loginDelay = 0;

    final int botAmount;
    if (!options.has(botAmountOption)) {
      System.out.println("Option 'bot-amount' required.");
      printHelp(parser);
      return;
    } else botAmount = options.valueOf(botAmountOption);

    initGui();
    while (!sessions.get()) {
      synchronized (sessions) {
        try {
          sessions.wait(5000);
        } catch (InterruptedException exception) {
        }
      }
    }

    final Queue<Runnable> lockQueue = new ArrayDeque<Runnable>();

    ExecutorService service = Executors.newFixedThreadPool(botAmount + (loginDelay > 0 ? 1 : 0));
    final Object firstWait = new Object();
    if (loginDelay > 0) {
      service.execute(
          new Runnable() {
            @Override
            public void run() {
              synchronized (firstWait) {
                try {
                  firstWait.wait();
                } catch (InterruptedException exception) {
                }
              }
              while (true) {
                if (die) return;
                while (slotsTaken.get() >= 2) {
                  synchronized (slotsTaken) {
                    try {
                      slotsTaken.wait(500);
                    } catch (InterruptedException exception) {
                    }
                  }
                }
                synchronized (lockQueue) {
                  if (lockQueue.size() > 0) {
                    Runnable thread = lockQueue.poll();
                    synchronized (thread) {
                      thread.notifyAll();
                    }
                    lockQueue.offer(thread);
                  } else continue;
                }
                try {
                  Thread.sleep(loginDelay);
                } catch (InterruptedException exception) {
                }
                while (!sessions.get()) {
                  synchronized (sessions) {
                    try {
                      sessions.wait(5000);
                    } catch (InterruptedException exception) {
                    }
                  }
                }
              }
            }
          });
    }
    final List<String> accountsInUse = new ArrayList<String>();
    for (int i = 0; i < botAmount; i++) {
      final int botNumber = i;
      Runnable runnable =
          new Runnable() {
            @Override
            public void run() {
              if (loginDelay > 0)
                synchronized (lockQueue) {
                  lockQueue.add(this);
                }
              Random random = new Random();

              if (!offline) {
                boolean authenticated = false;
                user:
                while (true) {
                  if (authenticated) {
                    authenticated = false;
                    sessionCount.decrementAndGet();
                  }
                  Session session = null;
                  String loginProxy;
                  String account = accounts.get(random.nextInt(accounts.size()));
                  synchronized (accountsInUse) {
                    if (accountsInUse.size() == accounts.size()) System.exit(0);
                    while (accountsInUse.contains(account))
                      account = accounts.get(random.nextInt(accounts.size()));
                    accountsInUse.add(account);
                  }
                  String[] accountParts = account.split(":");
                  while (true) {
                    while (!sessions.get()) {
                      synchronized (sessions) {
                        try {
                          sessions.wait(5000);
                        } catch (InterruptedException exception) {
                        }
                      }
                    }
                    loginProxy = httpProxies.get(random.nextInt(httpProxies.size()));
                    try {
                      session = Util.retrieveSession(accountParts[0], accountParts[1], loginProxy);
                      // addAccount(session);
                      sessionCount.incrementAndGet();
                      authenticated = true;
                      break;
                    } catch (AuthenticationException exception) {
                      System.err.println("[Bot" + botNumber + "] " + exception);
                      if (!exception.getMessage().startsWith("Exception"))
                        // && !exception.getMessage().equals(
                        // "Too many failed logins"))
                        continue user;
                    }
                  }
                  System.out.println(
                      "["
                          + session.getUsername()
                          + "] Password: "******", Session ID: "
                          + session.getSessionId());
                  while (!joins.get()) {
                    synchronized (joins) {
                      try {
                        joins.wait(5000);
                      } catch (InterruptedException exception) {
                      }
                    }
                  }
                  if (loginDelay > 0) {
                    synchronized (this) {
                      try {
                        synchronized (firstWait) {
                          firstWait.notifyAll();
                        }
                        wait();
                      } catch (InterruptedException exception) {
                      }
                    }
                  }

                  while (true) {
                    String proxy =
                        useProxy ? socksProxies.get(random.nextInt(socksProxies.size())) : null;
                    try {
                      new DarkBotMCSpambot(
                          DARK_BOT,
                          server,
                          session.getUsername(),
                          session.getPassword(),
                          session.getSessionId(),
                          null,
                          proxy,
                          owner);
                      if (die) break user;
                      else if (!autoRejoin) break;
                    } catch (Exception exception) {
                      exception.printStackTrace();
                      System.out.println(
                          "["
                              + session.getUsername()
                              + "] Error connecting: "
                              + exception.getCause().toString());
                    }
                  }
                  System.out.println("[" + session.getUsername() + "] Account failed");
                }
              } else {
                while (true) {
                  String proxy =
                      useProxy ? socksProxies.get(random.nextInt(socksProxies.size())) : null;
                  try {
                    String username = "";
                    if (accounts != null) {
                      username = accounts.get(random.nextInt(accounts.size())).split(":")[0];
                      synchronized (accountsInUse) {
                        while (accountsInUse.contains(username))
                          username = accounts.get(random.nextInt(accounts.size()));
                        accountsInUse.add(username);
                      }
                    } else
                      for (int i = 0; i < 10 + random.nextInt(6); i++)
                        username += alphas[random.nextInt(alphas.length)];
                    if (loginDelay > 0) {
                      synchronized (this) {
                        try {
                          synchronized (firstWait) {
                            firstWait.notifyAll();
                          }
                          wait();
                        } catch (InterruptedException exception) {
                        }
                      }
                    }
                    new DarkBotMCSpambot(DARK_BOT, server, username, "", "", null, proxy, owner);
                    if (die || !autoRejoin) break;
                    else continue;
                  } catch (Exception exception) {
                    System.out.println(
                        "[Bot" + botNumber + "] Error connecting: " + exception.toString());
                  }
                }
              }
            }
          };
      service.execute(runnable);
    }
    service.shutdown();
    while (!service.isTerminated()) {
      try {
        service.awaitTermination(9000, TimeUnit.DAYS);
      } catch (InterruptedException exception) {
        exception.printStackTrace();
      }
    }
    System.exit(0);
  }
 @EventHandler
 public void onPacketProcess(PacketProcessEvent event) {
   // System.out.println("Packet received: " + event.getPacket().getId()
   // + " (" + event.getPacket() + ")");
   Packet packet = event.getPacket();
   switch (packet.getId()) {
     case 0:
       connectionHandler.sendPacket(new Packet0KeepAlive(new Random().nextInt()));
       break;
     case 3:
       String message = ((Packet3Chat) packet).message;
       message = removeColors(message);
       System.out.println("[" + bot.getSession().getUsername() + "] " + message);
       String testMessage = "[MineCaptcha] To be unmuted answer this question: What is ";
       String testMessage2 = "Please type '";
       String testMessage3 = "' to continue sending messages/commands";
       if (message.contains(testMessage)) {
         try {
           String captcha = message.split(Pattern.quote(testMessage))[1].split("[ \\?]")[0];
           ScriptEngineManager mgr = new ScriptEngineManager();
           ScriptEngine engine = mgr.getEngineByName("JavaScript");
           String solved = engine.eval(captcha).toString();
           solved = solved.split("\\.")[0];
           connectionHandler.sendPacket(new Packet3Chat(solved));
         } catch (Exception exception) {
           exception.printStackTrace();
         }
       } else if (message.contains(testMessage2) && message.contains(testMessage3)) {
         try {
           String captcha =
               message.split(Pattern.quote(testMessage2))[1].split(Pattern.quote(testMessage3))[0];
           connectionHandler.sendPacket(new Packet3Chat(captcha));
         } catch (Exception exception) {
           exception.printStackTrace();
         }
       } else if (message.startsWith("Please register with \"/register")) {
         String password = "";
         for (int i = 0; i < 10 + random.nextInt(6); i++)
           password += alphas[random.nextInt(alphas.length)];
         bot.say("/register " + password + " " + password);
       } else if (message.startsWith("/uc ")) {
         connectionHandler.sendPacket(new Packet3Chat(message));
       } else if ((message.contains("do the crime") && message.contains("do the time"))
           || message.contains("You have been muted")) {
         connectionHandler.sendPacket(new Packet3Chat("\247Leaving!"));
       } else if (message.contains(owner + " has requested to teleport to you.")) {
         connectionHandler.sendPacket(new Packet3Chat("/tpaccept"));
       } else if (message.contains(owner)) {
         if (message.contains("Go ")) {
           spamMessage = message.substring(message.indexOf("Go ") + "Go ".length());
         } else if (message.contains("Stop")) {
           spamMessage = null;
           bot.getTaskManager().stopAll();
         } else if (message.contains("Die")) {
           die = true;
         } else if (message.contains("Say ")) {
           connectionHandler.sendPacket(
               new Packet3Chat(message.substring(message.indexOf("Say ") + "Say ".length())));
         } else if (message.contains("Leave")) {
           connectionHandler.sendPacket(new Packet255KickDisconnect("Quit"));
         } else if (message.contains("Tool")) {
           MainPlayerEntity player = bot.getPlayer();
           if (player == null) return;
           PlayerInventory inventory = player.getInventory();
           inventory.setCurrentHeldSlot(
               Integer.parseInt(
                   message.substring(message.indexOf("Tool ") + "Tool ".length()).split(" ")[0]));
         } else if (message.contains("DropId ")) {
           MainPlayerEntity player = bot.getPlayer();
           PlayerInventory inventory = player.getInventory();
           String substring =
               message.substring(message.indexOf("DropId ") + "DropId ".length()).split(" ")[0];
           int id = Integer.parseInt(substring);
           for (int slot = 0; slot < 40; slot++) {
             ItemStack item = inventory.getItemAt(slot);
             if (item != null && item.getId() == id) {
               inventory.selectItemAt(slot, true);
               inventory.dropSelectedItem();
             }
           }
           inventory.close();
         } else if (message.contains("Drop")) {
           MainPlayerEntity player = bot.getPlayer();
           PlayerInventory inventory = player.getInventory();
           if (message.contains("Drop ")) {
             String substring =
                 message.substring(message.indexOf("Drop ") + "Drop ".length()).split(" ")[0];
             try {
               int slot = Integer.parseInt(substring);
               if (slot < 0 || slot >= 40) return;
               if (inventory.getItemAt(slot) != null) {
                 inventory.selectItemAt(slot, true);
                 inventory.dropSelectedItem();
               }
               return;
             } catch (NumberFormatException e) {
             }
           }
           for (int slot = 0; slot < 40; slot++) {
             if (inventory.getItemAt(slot) != null) {
               inventory.selectItemAt(slot, true);
               inventory.dropSelectedItem();
             }
           }
           inventory.close();
         } else if (message.contains("Switch ")) {
           MainPlayerEntity player = bot.getPlayer();
           PlayerInventory inventory = player.getInventory();
           String substring = message.substring(message.indexOf("Switch ") + "Switch ".length());
           try {
             int slot1 = Integer.parseInt(substring.split(" ")[0]);
             int slot2 = Integer.parseInt(substring.split(" ")[1]);
             if (slot1 < 0 || slot1 >= 45 || slot2 < 0 || slot2 >= 45) return;
             inventory.selectItemAt(slot1);
             inventory.selectItemAt(slot2);
             inventory.selectItemAt(slot1);
           } catch (NumberFormatException e) {
           }
           // inventory.close();
         } else if (message.contains("Equip")) {
           MainPlayerEntity player = bot.getPlayer();
           PlayerInventory inventory = player.getInventory();
           boolean helmet = inventory.getArmorAt(0) != null;
           boolean chestplate = inventory.getArmorAt(1) != null;
           boolean leggings = inventory.getArmorAt(2) != null;
           boolean boots = inventory.getArmorAt(3) != null;
           boolean changed = false;
           for (int i = 0; i < 36; i++) {
             ItemStack item = inventory.getItemAt(i);
             if (item == null) continue;
             int armorSlot;
             int id = item.getId();
             if (!helmet
                 && (id == 86 || id == 298 || id == 302 || id == 306 || id == 310 || id == 314)) {
               armorSlot = 0;
               helmet = true;
             } else if (!chestplate
                 && (id == 299 || id == 303 || id == 307 || id == 311 || id == 315)) {
               armorSlot = 1;
               chestplate = true;
             } else if (!leggings
                 && (id == 300 || id == 304 || id == 308 || id == 312 || id == 316)) {
               armorSlot = 2;
               leggings = true;
             } else if (!boots
                 && (id == 301 || id == 305 || id == 309 || id == 313 || id == 317)) {
               armorSlot = 3;
               boots = true;
             } else if (helmet && chestplate && leggings && boots) break;
             else continue;
             inventory.selectItemAt(i);
             inventory.selectArmorAt(armorSlot);
             changed = true;
           }
           if (!changed) {
             for (int i = 0; i < 36; i++) {
               ItemStack item = inventory.getItemAt(i);
               if (item != null) continue;
               int armorSlot;
               if (helmet) {
                 armorSlot = 0;
                 helmet = false;
               } else if (chestplate) {
                 armorSlot = 1;
                 chestplate = false;
               } else if (leggings) {
                 armorSlot = 2;
                 leggings = false;
               } else if (boots) {
                 armorSlot = 3;
                 boots = false;
               } else if (!helmet && !chestplate && !leggings && !boots) break;
               else continue;
               inventory.selectArmorAt(armorSlot);
               inventory.selectItemAt(i);
             }
           }
           inventory.close();
           bot.say("Equipped armor.");
         } else if (message.contains("Owner ")) {
           String name =
               message.substring(message.indexOf("Owner ") + "Owner ".length()).split(" ")[0];
           owner = name;
           bot.say("Set owner to " + name);
         }
       } else if (message.contains("You are not member of any faction.")
           && spamMessage != null
           && createFaction) {
         String msg = "/f create ";
         for (int i = 0; i < 7 + random.nextInt(3); i++)
           msg += alphas[random.nextInt(alphas.length)];
         bot.say(msg);
       }
       if (message.matches("[\\*]*SPQR [\\w]{1,16} invited you to SPQR")) {
         bot.say("/f join SPQR");
         bot.say("\247asdf");
       }
       break;
     case 8:
       Packet8UpdateHealth updateHealth = (Packet8UpdateHealth) packet;
       if (updateHealth.healthMP <= 0) connectionHandler.sendPacket(new Packet205ClientCommand(1));
       break;
     case 9:
       TaskManager taskManager = bot.getTaskManager();
       taskManager.stopAll();
       break;
   }
 }
  public static void main(String[] args) {
    // TODO main
    OptionParser parser = new OptionParser();
    parser.acceptsAll(Arrays.asList("h", "help"), "Show this help dialog.");
    OptionSpec<String> serverOption =
        parser
            .acceptsAll(Arrays.asList("s", "server"), "Server to join.")
            .withRequiredArg()
            .describedAs("server-address[:port]");
    OptionSpec<String> proxyOption =
        parser
            .acceptsAll(
                Arrays.asList("P", "proxy"),
                "SOCKS proxy to use. Ignored in presence of 'socks-proxy-list'.")
            .withRequiredArg()
            .describedAs("proxy-address");
    OptionSpec<String> ownerOption =
        parser
            .acceptsAll(
                Arrays.asList("o", "owner"), "Owner of the bot (username of in-game control).")
            .withRequiredArg()
            .describedAs("username");
    OptionSpec<?> offlineOption =
        parser.acceptsAll(
            Arrays.asList("O", "offline"),
            "Offline-mode. Ignores 'password' and 'account-list' (will "
                + "generate random usernames if 'username' is not supplied).");
    OptionSpec<?> autoRejoinOption =
        parser.acceptsAll(Arrays.asList("a", "auto-rejoin"), "Auto-rejoin a server on disconnect.");
    OptionSpec<Integer> loginDelayOption =
        parser
            .acceptsAll(
                Arrays.asList("d", "login-delay"),
                "Delay between bot joins, in milliseconds. 5000 is "
                    + "recommended if not using socks proxies.")
            .withRequiredArg()
            .describedAs("delay")
            .ofType(Integer.class);
    OptionSpec<Integer> botAmountOption =
        parser
            .acceptsAll(
                Arrays.asList("b", "bot-amount"),
                "Amount of bots to join. Must be <= amount of accounts.")
            .withRequiredArg()
            .describedAs("amount")
            .ofType(Integer.class);
    OptionSpec<String> protocolOption =
        parser
            .accepts(
                "protocol",
                "Protocol version to use. Can be either protocol number or Minecraft version.")
            .withRequiredArg();
    OptionSpec<?> protocolsOption =
        parser.accepts("protocols", "List available protocols and exit.");

    OptionSpec<String> accountListOption =
        parser
            .accepts(
                "account-list",
                "File containing a list of accounts, in username/email:password format.")
            .withRequiredArg()
            .describedAs("file");
    OptionSpec<String> socksProxyListOption =
        parser
            .accepts(
                "socks-proxy-list",
                "File containing a list of SOCKS proxies, in address:port format.")
            .withRequiredArg()
            .describedAs("file");
    OptionSpec<String> httpProxyListOption =
        parser
            .accepts(
                "http-proxy-list",
                "File containing a list of HTTP proxies, in address:port format.")
            .withRequiredArg()
            .describedAs("file");
    OptionSpec<String> captchaListOption =
        parser
            .accepts("captcha-list", "File containing a list of chat baised captcha to bypass.")
            .withRequiredArg()
            .describedAs("file");

    OptionSet options;
    try {
      options = parser.parse(args);
    } catch (OptionException exception) {
      try {
        parser.printHelpOn(System.out);
      } catch (Exception exception1) {
        exception1.printStackTrace();
      }
      return;
    }

    if (options.has("help")) {
      printHelp(parser);
      return;
    }
    if (options.has(protocolsOption)) {
      System.out.println("Available protocols:");
      for (ProtocolProvider provider : ProtocolProvider.getProviders())
        System.out.println(
            "\t"
                + provider.getMinecraftVersion()
                + " ("
                + provider.getSupportedVersion()
                + "): "
                + provider.getClass().getName());
      System.out.println(
          "If no protocols are listed above, you may attempt to specify a protocol version in case the provider is actually in the class-path.");
      return;
    }

    final boolean offline = options.has(offlineOption);
    final boolean autoRejoin = options.has(autoRejoinOption);

    final List<String> accounts;
    if (options.has(accountListOption)) {
      accounts = loadAccounts(options.valueOf(accountListOption));
    } else if (!offline) {
      System.out.println("Option 'accounts' must be supplied in " + "absence of option 'offline'.");
      printHelp(parser);
      return;
    } else accounts = null;

    final List<String> captcha;
    if (options.has(captchaListOption)) readCaptchaFile(options.valueOf(captchaListOption));

    final String server;
    if (!options.has(serverOption)) {
      System.out.println("Option 'server' required.");
      printHelp(parser);
      return;
    } else server = options.valueOf(serverOption);

    final String owner;
    if (!options.has(ownerOption)) {
      System.out.println("Option 'owner' required.");
      printHelp(parser);
      return;
    } else owner = options.valueOf(ownerOption);

    final int protocol;
    if (options.has(protocolOption)) {
      String protocolString = options.valueOf(protocolOption);
      int parsedProtocol;
      try {
        parsedProtocol = Integer.parseInt(protocolString);
      } catch (NumberFormatException exception) {
        ProtocolProvider foundProvider = null;
        for (ProtocolProvider provider : ProtocolProvider.getProviders())
          if (protocolString.equals(provider.getMinecraftVersion())) foundProvider = provider;
        if (foundProvider == null) {
          System.out.println("No provider found for Minecraft version '" + protocolString + "'.");
          return;
        } else parsedProtocol = foundProvider.getSupportedVersion();
      }
      protocol = parsedProtocol;
    } else protocol = MinecraftBot.LATEST_PROTOCOL;

    final List<String> socksProxies;
    if (options.has(socksProxyListOption))
      socksProxies = loadProxies(options.valueOf(socksProxyListOption));
    else socksProxies = null;
    final boolean useProxy = socksProxies != null;

    final List<String> httpProxies;
    if (options.has(httpProxyListOption))
      httpProxies = loadLoginProxies(options.valueOf(httpProxyListOption));
    else if (!offline && accounts != null) {
      System.out.println(
          "Option 'http-proxy-list' required if " + "option 'account-list' is supplied.");
      printHelp(parser);
      return;
    } else httpProxies = null;

    final int loginDelay;
    if (options.has(loginDelayOption)) loginDelay = options.valueOf(loginDelayOption);
    else loginDelay = 0;

    final int botAmount;
    if (!options.has(botAmountOption)) {
      System.out.println("Option 'bot-amount' required.");
      printHelp(parser);
      return;
    } else botAmount = options.valueOf(botAmountOption);

    initGui();
    while (!sessions.get()) {
      synchronized (sessions) {
        try {
          sessions.wait(5000);
        } catch (InterruptedException exception) {
        }
      }
    }

    final Queue<Runnable> lockQueue = new ArrayDeque<Runnable>();

    ExecutorService service = Executors.newFixedThreadPool(botAmount + (loginDelay > 0 ? 1 : 0));
    final Object firstWait = new Object();
    if (loginDelay > 0) {
      service.execute(
          new Runnable() {
            @Override
            public void run() {
              synchronized (firstWait) {
                try {
                  firstWait.wait();
                } catch (InterruptedException exception) {
                }
              }
              while (true) {
                try {
                  Thread.sleep(loginDelay);
                } catch (InterruptedException exception) {
                }
                synchronized (lockQueue) {
                  if (lockQueue.size() > 0) {
                    Runnable thread = lockQueue.poll();
                    synchronized (thread) {
                      thread.notifyAll();
                    }
                    lockQueue.offer(thread);
                  } else continue;
                }
                while (!sessions.get()) {
                  synchronized (sessions) {
                    try {
                      sessions.wait(5000);
                    } catch (InterruptedException exception) {
                    }
                  }
                }
              }
            }
          });
    }
    final List<String> accountsInUse = new ArrayList<String>();
    final Map<String, AtomicInteger> workingProxies = new HashMap<String, AtomicInteger>();
    for (int i = 0; i < botAmount; i++) {
      final int botNumber = i;
      Runnable runnable =
          new Runnable() {
            @Override
            public void run() {
              if (loginDelay > 0)
                synchronized (lockQueue) {
                  lockQueue.add(this);
                }
              Random random = new Random();

              if (!offline) {
                AuthService authService = new LegacyAuthService();
                boolean authenticated = false;
                user:
                while (true) {
                  if (authenticated) {
                    authenticated = false;
                    sessionCount.decrementAndGet();
                  }
                  Session session = null;
                  String loginProxy;
                  String account = accounts.get(random.nextInt(accounts.size()));
                  synchronized (accountsInUse) {
                    if (accountsInUse.size() == accounts.size()) System.exit(0);
                    while (accountsInUse.contains(account))
                      account = accounts.get(random.nextInt(accounts.size()));
                    accountsInUse.add(account);
                  }
                  String[] accountParts = account.split(":");
                  while (true) {
                    while (!sessions.get()) {
                      synchronized (sessions) {
                        try {
                          sessions.wait(5000);
                        } catch (InterruptedException exception) {
                        }
                      }
                    }
                    synchronized (workingProxies) {
                      Iterator<String> iterator = workingProxies.keySet().iterator();
                      if (iterator.hasNext()) loginProxy = iterator.next();
                      else loginProxy = httpProxies.get(random.nextInt(httpProxies.size()));
                      ;
                    }
                    try {
                      session =
                          authService.login(
                              accountParts[0],
                              accountParts[1],
                              toProxy(loginProxy, Proxy.Type.HTTP));
                      // addAccount(session);
                      synchronized (workingProxies) {
                        AtomicInteger count = workingProxies.get(loginProxy);
                        if (count != null) count.set(0);
                        else workingProxies.put(loginProxy, new AtomicInteger());
                      }
                      sessionCount.incrementAndGet();
                      authenticated = true;
                      break;
                    } catch (IOException exception) {
                      synchronized (workingProxies) {
                        workingProxies.remove(loginProxy);
                      }
                      System.err.println("[Bot" + botNumber + "] " + loginProxy + ": " + exception);
                    } catch (AuthenticationException exception) {
                      if (exception.getMessage().contains("Too many failed logins")) {
                        synchronized (workingProxies) {
                          AtomicInteger count = workingProxies.get(loginProxy);
                          if (count != null && count.incrementAndGet() >= 5)
                            workingProxies.remove(loginProxy);
                        }
                      }
                      System.err.println("[Bot" + botNumber + "] " + loginProxy + ": " + exception);
                      continue user;
                    }
                  }
                  System.out.println("[" + session.getUsername() + "] " + session);
                  while (!joins.get()) {
                    synchronized (joins) {
                      try {
                        joins.wait(5000);
                      } catch (InterruptedException exception) {
                      }
                    }
                  }
                  if (loginDelay > 0) {
                    synchronized (this) {
                      try {
                        synchronized (firstWait) {
                          firstWait.notifyAll();
                        }
                        wait();
                      } catch (InterruptedException exception) {
                      }
                    }
                  }

                  while (true) {
                    String proxy =
                        useProxy ? socksProxies.get(random.nextInt(socksProxies.size())) : null;
                    try {
                      DarkBotMCSpambot bot =
                          new DarkBotMCSpambot(
                              generateData(
                                  server,
                                  session.getUsername(),
                                  session.getPassword(),
                                  authService,
                                  session,
                                  protocol,
                                  null,
                                  proxy),
                              owner);
                      while (bot.getBot().isConnected()) {
                        try {
                          Thread.sleep(500);
                        } catch (InterruptedException exception) {
                          exception.printStackTrace();
                        }
                      }
                      if (!autoRejoin) break;
                    } catch (Exception exception) {
                      exception.printStackTrace();
                      System.out.println(
                          "["
                              + session.getUsername()
                              + "] Error connecting: "
                              + exception.getCause().toString());
                    }
                  }
                  System.out.println("[" + session.getUsername() + "] Account failed");
                }
              } else {
                while (true) {
                  String proxy =
                      useProxy ? socksProxies.get(random.nextInt(socksProxies.size())) : null;
                  try {
                    String username;
                    if (accounts != null) {
                      username = accounts.get(random.nextInt(accounts.size())).split(":")[0];
                      synchronized (accountsInUse) {
                        while (accountsInUse.contains(username))
                          username = accounts.get(random.nextInt(accounts.size()));
                        accountsInUse.add(username);
                      }
                    } else username = Util.generateRandomString(10 + random.nextInt(6));
                    if (loginDelay > 0) {
                      synchronized (this) {
                        try {
                          synchronized (firstWait) {
                            firstWait.notifyAll();
                          }
                          wait();
                        } catch (InterruptedException exception) {
                        }
                      }
                    }
                    DarkBotMCSpambot bot =
                        new DarkBotMCSpambot(
                            generateData(server, username, null, null, null, protocol, null, proxy),
                            owner);
                    while (bot.getBot().isConnected()) {
                      try {
                        Thread.sleep(500);
                      } catch (InterruptedException exception) {
                        exception.printStackTrace();
                      }
                    }
                    if (!autoRejoin) break;
                    else continue;
                  } catch (Exception exception) {
                    System.out.println(
                        "[Bot" + botNumber + "] Error connecting: " + exception.toString());
                  }
                }
              }
            }
          };
      service.execute(runnable);
    }
    service.shutdown();
    while (!service.isTerminated()) {
      try {
        service.awaitTermination(9000, TimeUnit.DAYS);
      } catch (InterruptedException exception) {
        exception.printStackTrace();
      }
    }
    System.exit(0);
  }