@EventHandler(priority = EventPriority.HIGHEST)
  public void onCommand(PlayerCommandPreprocessEvent pcpe) {
    Player player = pcpe.getPlayer();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.STOPPED) {
      return;
    }

    String message = pcpe.getMessage();
    if (!message.startsWith("/" + Main.getInstance().getStringConfig("command-prefix", "bw"))) {

      for (String allowed : Main.getInstance().getAllowedCommands()) {
        if (!allowed.startsWith("/")) {
          allowed = "/" + allowed;
        }

        if (message.startsWith(allowed.trim())) {
          return;
        }
      }

      if (player.hasPermission("bw.cmd")) {
        return;
      }

      pcpe.setCancelled(true);
      return;
    }
  }
  @EventHandler
  public void onSwitchWorld(PlayerChangedWorldEvent change) {
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(change.getPlayer());
    if (game != null) {
      if (game.getState() == GameState.RUNNING) {
        if (!game.getCycle().isEndGameRunning()) {
          if (!game.getPlayerSettings(change.getPlayer()).isTeleporting()) {
            game.playerLeave(change.getPlayer(), false);
          } else {
            game.getPlayerSettings(change.getPlayer()).setTeleporting(false);
          }
        }
      } else if (game.getState() == GameState.WAITING) {
        if (!game.getPlayerSettings(change.getPlayer()).isTeleporting()) {
          game.playerLeave(change.getPlayer(), false);
        } else {
          game.getPlayerSettings(change.getPlayer()).setTeleporting(false);
        }
      }
    }

    if (!Main.getInstance().isHologramsEnabled()
        || Main.getInstance().getHolographicInteractor() == null) {
      return;
    }

    Main.getInstance().getHolographicInteractor().updateHolograms(change.getPlayer());
  }
  @EventHandler
  public void onEntitySpawn(CreatureSpawnEvent ese) {
    if (Main.getInstance().getGameManager() == null) {
      return;
    }

    if (ese.getLocation() == null) {
      return;
    }

    if (ese.getLocation().getWorld() == null) {
      return;
    }

    Game game = Main.getInstance().getGameManager().getGameByLocation(ese.getLocation());
    if (game == null) {
      return;
    }

    if (game.getState() == GameState.STOPPED) {
      return;
    }

    if (ese.getEntityType().equals(EntityType.CREEPER)
        || ese.getEntityType().equals(EntityType.CAVE_SPIDER)
        || ese.getEntityType().equals(EntityType.SPIDER)
        || ese.getEntityType().equals(EntityType.ZOMBIE)
        || ese.getEntityType().equals(EntityType.SKELETON)
        || ese.getEntityType().equals(EntityType.SILVERFISH)) {
      ese.setCancelled(true);
    }
  }
  @EventHandler(priority = EventPriority.HIGH)
  public void onHunger(FoodLevelChangeEvent flce) {
    if (!(flce.getEntity() instanceof Player)) {
      return;
    }

    Player player = (Player) flce.getEntity();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.RUNNING) {
      if (game.isSpectator(player)) {
        flce.setCancelled(true);
        return;
      }

      flce.setCancelled(false);
      return;
    }

    flce.setCancelled(true);
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onJoin(PlayerJoinEvent je) {
    if (Main.getInstance().isBungee()) {
      je.setJoinMessage("");
      ArrayList<Game> games = Main.getInstance().getGameManager().getGames();
      if (games.size() == 0) {
        return;
      }

      Player player = je.getPlayer();
      Game firstGame = games.get(0);

      if (firstGame.getState() == GameState.STOPPED) {
        return;
      }

      if (!firstGame.playerJoins(player)) {
        if (firstGame.getCycle() instanceof BungeeGameCycle) {
          ((BungeeGameCycle) firstGame.getCycle())
              .bungeeSendToServer(Main.getInstance().getBungeeHub(), player, true);
        }
      }
    }

    if (Main.getInstance().isHologramsEnabled()
        && Main.getInstance().getHolographicInteractor() != null) {
      Main.getInstance().getHolographicInteractor().updateHolograms(je.getPlayer(), 60L);
    }
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onEntityDamageByEntity(EntityDamageByEntityEvent ede) {
    List<EntityType> canDamageTypes = new ArrayList<EntityType>();
    canDamageTypes.add(EntityType.PLAYER);

    if (Main.getInstance().getServer().getPluginManager().isPluginEnabled("AntiAura")) {
      canDamageTypes.add(EntityType.SQUID);
    }

    if (canDamageTypes.contains(ede.getEntityType())) {
      return;
    }

    Game game =
        Main.getInstance().getGameManager().getGameByLocation(ede.getEntity().getLocation());
    if (game == null) {
      return;
    }

    if (game.getState() == GameState.STOPPED) {
      return;
    }

    ede.setCancelled(true);
  }
  @SuppressWarnings("deprecation")
  private void onLobbyInventoryClick(InventoryClickEvent ice, Player player, Game game) {
    Inventory inv = ice.getInventory();
    ItemStack clickedStack = ice.getCurrentItem();

    if (!inv.getTitle().equals(Main._l("lobby.chooseteam"))) {
      ice.setCancelled(true);
      return;
    }

    if (clickedStack == null) {
      ice.setCancelled(true);
      return;
    }

    if (clickedStack.getType() != Material.WOOL) {
      return;
    }

    ice.setCancelled(true);
    Team team = game.getTeamByDyeColor(DyeColor.getByData(clickedStack.getData().getData()));
    if (team == null) {
      return;
    }

    game.playerJoinTeam(player, team);
    player.closeInventory();
  }
  private void inGameInteractEntity(PlayerInteractEntityEvent iee, Game game, Player player) {

    if (iee.getPlayer().getItemInHand().getType().equals(Material.MONSTER_EGG)
        || iee.getPlayer().getItemInHand().getType().equals(Material.MONSTER_EGGS)
        || iee.getPlayer().getItemInHand().getType().equals(Material.DRAGON_EGG)) {
      iee.setCancelled(true);
      return;
    }

    if (iee.getRightClicked() != null) {
      if (!iee.getRightClicked().getType().equals(EntityType.VILLAGER)) {
        List<EntityType> preventClickTypes = Arrays.asList(EntityType.ITEM_FRAME);

        // armor stand in 1.8
        try {
          preventClickTypes.add(EntityType.valueOf("ARMOR_STAND"));
        } catch (Exception ex) {
          // nothing will happen, just not supported
        }

        if (preventClickTypes.contains(iee.getRightClicked().getType())) {
          iee.setCancelled(true);
        }

        return;
      }
    }

    iee.setCancelled(true);

    if (game.isSpectator(player)) {
      return;
    }

    BedwarsOpenShopEvent openShopEvent =
        new BedwarsOpenShopEvent(game, player, game.getItemShopCategories(), iee.getRightClicked());
    Main.getInstance().getServer().getPluginManager().callEvent(openShopEvent);

    if (openShopEvent.isCancelled()) {
      return;
    }

    if (game.isUsingOldShop(player)) {
      MerchantCategory.openCategorySelection(player, game);
    } else {
      NewItemShop itemShop = game.getNewItemShop(player);
      if (itemShop == null) {
        itemShop = game.openNewItemShop(player);
      }

      itemShop.setCurrentCategory(null);
      itemShop.openCategoryInventory(player);
    }
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onQuit(PlayerQuitEvent pqe) {
    Player player = pqe.getPlayer();
    Game g = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (g == null) {
      return;
    }

    g.playerLeave(player, false);
  }
  @EventHandler
  public void onSleep(PlayerBedEnterEvent bee) {

    Player p = bee.getPlayer();

    Game g = Main.getInstance().getGameManager().getGameOfPlayer(p);
    if (g == null) {
      return;
    }

    if (g.getState() == GameState.STOPPED) {
      return;
    }

    bee.setCancelled(true);
  }
  @EventHandler
  public void onInventoryClick(InventoryClickEvent ice) {
    Player player = (Player) ice.getWhoClicked();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.WAITING) {
      this.onLobbyInventoryClick(ice, player, game);
    }

    if (game.getState() == GameState.RUNNING) {
      this.onIngameInventoryClick(ice, player, game);
    }
  }
  @EventHandler
  public void onInteractEntity(PlayerInteractEntityEvent iee) {
    Player p = iee.getPlayer();
    Game g = Main.getInstance().getGameManager().getGameOfPlayer(p);
    if (g == null) {
      return;
    }

    if (g.getState() == GameState.WAITING) {
      iee.setCancelled(true);
      return;
    }

    if (g.getState() == GameState.RUNNING) {
      this.inGameInteractEntity(iee, g, p);
    }
  }
  @EventHandler
  public void onDrop(PlayerDropItemEvent die) {
    Player p = die.getPlayer();
    Game g = Main.getInstance().getGameManager().getGameOfPlayer(p);
    if (g == null) {
      return;
    }

    if (g.getState() != GameState.WAITING) {
      if (g.isSpectator(p)) {
        die.setCancelled(true);
      }

      return;
    }

    die.setCancelled(true);
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onPlayerRespawn(PlayerRespawnEvent pre) {
    Player p = pre.getPlayer();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(p);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.RUNNING) {
      game.getCycle().onPlayerRespawn(pre, p);
      return;
    }

    if (game.getState() == GameState.WAITING) {
      pre.setRespawnLocation(game.getLobby());
    }
  }
  @EventHandler
  public void onPickup(PlayerPickupItemEvent ppie) {
    Player player = ppie.getPlayer();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() != GameState.WAITING) {
      if (game.isSpectator(player)) {
        ppie.setCancelled(true);
      }

      return;
    }

    ppie.setCancelled(true);
  }
  @EventHandler
  public void onCraft(CraftItemEvent cie) {
    Player player = (Player) cie.getWhoClicked();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.STOPPED) {
      return;
    }

    if (Main.getInstance().getBooleanConfig("allow-crafting", false)) {
      return;
    }

    cie.setCancelled(true);
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onFly(PlayerToggleFlightEvent tfe) {
    Player p = tfe.getPlayer();

    Game g = Main.getInstance().getGameManager().getGameOfPlayer(p);
    if (g == null) {
      return;
    }

    if (g.getState() == GameState.STOPPED) {
      return;
    }

    if (g.getState() == GameState.RUNNING && g.isSpectator(p)) {
      tfe.setCancelled(false);
      return;
    }

    tfe.setCancelled(true);
  }
  @EventHandler(priority = EventPriority.HIGH)
  public void onRegainHealth(EntityRegainHealthEvent rhe) {
    if (rhe.getEntityType() != EntityType.PLAYER) {
      return;
    }

    Player player = (Player) rhe.getEntity();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() != GameState.RUNNING) {
      return;
    }

    if (player.getHealth() >= player.getMaxHealth()) {
      game.setPlayerDamager(player, null);
    }
  }
  @EventHandler
  public void openInventory(InventoryOpenEvent ioe) {
    if (!(ioe.getPlayer() instanceof Player)) {
      return;
    }

    Player player = (Player) ioe.getPlayer();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() != GameState.RUNNING) {
      return;
    }

    if (ioe.getInventory().getType() == InventoryType.ENCHANTING
        || ioe.getInventory().getType() == InventoryType.BREWING
        || (ioe.getInventory().getType() == InventoryType.CRAFTING
            && !Main.getInstance().getBooleanConfig("allow-crafting", false))) {
      ioe.setCancelled(true);
      return;
    } else if (ioe.getInventory().getType() == InventoryType.CRAFTING
        && Main.getInstance().getBooleanConfig("allow-crafting", false)) {
      return;
    }

    if (game.isSpectator(player)) {
      if (ioe.getInventory().getName().equals(Main._l("ingame.spectator"))) {
        return;
      }

      ioe.setCancelled(true);
    }

    if (ioe.getInventory().getHolder() == null) {
      return;
    }

    if (game.getRegion().getInventories().contains(ioe.getInventory())) {
      return;
    }

    InventoryHolder holder = ioe.getInventory().getHolder();
    for (Class<?> interfaze : holder.getClass().getInterfaces()) {

      if (interfaze.equals(BlockState.class)) {
        game.getRegion().addInventory(ioe.getInventory());
        return;
      }

      for (Class<?> interfaze2 : interfaze.getInterfaces()) {
        if (interfaze2.equals(BlockState.class)) {
          game.getRegion().addInventory(ioe.getInventory());
          return;
        }
      }
    }
  }
  @EventHandler
  public void onEntityInteract(EntityInteractEvent event) {
    if (!(event.getEntity() instanceof Player)) {
      return;
    }

    if (event.getBlock().getType() != Material.SOIL
        && event.getBlock().getType() != Material.WHEAT) {
      return;
    }

    Player player = (Player) event.getEntity();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.WAITING) {
      event.setCancelled(true);
    }
  }
  @Override
  public boolean execute(CommandSender sender, ArrayList<String> args) {
    if (!sender.hasPermission("bw." + this.getPermission())) {
      return false;
    }

    Player player = (Player) sender;

    Game game = this.getPlugin().getGameManager().getGame(args.get(0));
    String name = args.get(1).toString();

    if (game == null) {
      player.sendMessage(
          ChatWriter.pluginMessage(
              ChatColor.RED
                  + Main._l(
                      "errors.gamenotfound", ImmutableMap.of("game", args.get(0).toString()))));
      return false;
    }

    if (game.getState() == GameState.RUNNING) {
      sender.sendMessage(
          ChatWriter.pluginMessage(ChatColor.RED + Main._l("errors.notwhilegamerunning")));
      return false;
    }

    if (name.length() > 15) {
      player.sendMessage(
          ChatWriter.pluginMessage(ChatColor.RED + Main._l("errors.toolongregionname")));
      return true;
    }

    game.setRegionName(name);
    player.sendMessage(
        ChatWriter.pluginMessage(ChatColor.GREEN + Main._l("success.regionnameset")));
    return true;
  }
  @EventHandler
  public void onPlayerInteract(PlayerInteractEvent pie) {
    Player player = pie.getPlayer();
    Game g = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (g == null) {
      if (pie.getAction() != Action.RIGHT_CLICK_BLOCK
          && pie.getAction() != Action.RIGHT_CLICK_AIR) {
        return;
      }

      Block clicked = pie.getClickedBlock();

      if (clicked == null) {
        return;
      }

      if (!(clicked.getState() instanceof Sign)) {
        return;
      }

      Game game = Main.getInstance().getGameManager().getGameBySignLocation(clicked.getLocation());
      if (game == null) {
        return;
      }

      if (game.playerJoins(player)) {
        player.sendMessage(ChatWriter.pluginMessage(ChatColor.GREEN + Main._l("success.joined")));
      }
      return;
    }

    if (g.getState() == GameState.STOPPED) {
      return;
    }

    Material interactingMaterial = pie.getMaterial();
    Block clickedBlock = pie.getClickedBlock();

    if (g.getState() == GameState.RUNNING) {
      if (pie.getAction() == Action.PHYSICAL) {
        if (clickedBlock != null
            && (clickedBlock.getType() == Material.WHEAT
                || clickedBlock.getType() == Material.SOIL)) {
          pie.setCancelled(true);
          return;
        }
      }

      if (pie.getAction() != Action.RIGHT_CLICK_BLOCK
          && pie.getAction() != Action.RIGHT_CLICK_AIR) {
        return;
      }

      if (clickedBlock != null) {
        if (clickedBlock.getType() == Material.LEVER
            && !g.isSpectator(player)
            && pie.getAction() == Action.RIGHT_CLICK_BLOCK) {
          if (!g.getRegion().isPlacedUnbreakableBlock(clickedBlock)) {
            g.getRegion().addPlacedUnbreakableBlock(clickedBlock, clickedBlock.getState());
          }
          return;
        }
      }

      if (g.isSpectator(player)) {
        if (interactingMaterial == Material.SLIME_BALL) {
          g.playerLeave(player, false);
          return;
        }

        if (interactingMaterial == Material.COMPASS) {
          g.openSpectatorCompass(player);
          pie.setCancelled(true);
          return;
        }
      }

      // Spectators want to block
      if (clickedBlock != null) {
        try {
          GameMode.valueOf("SPECTATOR");
        } catch (Exception ex) {
          for (Player p : g.getFreePlayers()) {
            if (!g.getRegion().isInRegion(p.getLocation())) {
              continue;
            }

            if (pie.getClickedBlock().getLocation().distance(p.getLocation()) < 2) {
              Location oldLocation = p.getLocation();
              if (oldLocation.getY() >= pie.getClickedBlock().getLocation().getY()) {
                oldLocation.setY(oldLocation.getY() + 2);
              } else {
                oldLocation.setY(oldLocation.getY() - 2);
              }

              p.teleport(oldLocation);
            }
          }
        }
      }

      if (clickedBlock != null) {
        if (clickedBlock.getType() == Material.ENDER_CHEST && !g.isSpectator(player)) {
          pie.setCancelled(true);

          Block chest = pie.getClickedBlock();
          Team chestTeam = g.getTeamOfEnderChest(chest);
          Team playerTeam = g.getPlayerTeam(player);

          if (chestTeam == null) {
            return;
          }

          if (chestTeam.equals(playerTeam)) {
            player.openInventory(chestTeam.getInventory());
          } else {
            player.sendMessage(
                ChatWriter.pluginMessage(ChatColor.RED + Main._l("ingame.noturteamchest")));
          }

          return;
        }
      }

      return;
    } else if (g.getState() == GameState.WAITING) {
      if (interactingMaterial == null) {
        pie.setCancelled(true);
        return;
      }

      if (pie.getAction() == Action.PHYSICAL) {
        if (clickedBlock != null
            && (clickedBlock.getType() == Material.WHEAT
                || clickedBlock.getType() == Material.SOIL)) {
          pie.setCancelled(true);
          return;
        }
      }

      if (pie.getAction() != Action.RIGHT_CLICK_BLOCK
          && pie.getAction() != Action.RIGHT_CLICK_AIR) {
        return;
      }

      switch (interactingMaterial) {
        case BED:
          pie.setCancelled(true);
          if (!g.isAutobalanceEnabled()) {
            g.getPlayerStorage(player).openTeamSelection(g);
          }

          break;
        case DIAMOND:
          pie.setCancelled(true);
          if (player.isOp() || player.hasPermission("bw.setup")) {
            g.start(player);
          } else if (player.hasPermission("bw.vip.forcestart")) {
            GameLobbyCountdownRule rule = Main.getInstance().getLobbyCountdownRule();
            if (rule.isRuleMet(g)) {
              g.start(player);
            } else {
              if (rule == GameLobbyCountdownRule.PLAYERS_IN_GAME
                  || rule == GameLobbyCountdownRule.ENOUGH_TEAMS_AND_PLAYERS) {
                player.sendMessage(
                    ChatWriter.pluginMessage(
                        ChatColor.RED + Main._l("lobby.notenoughplayers-rule0")));
              } else {
                player.sendMessage(
                    ChatWriter.pluginMessage(
                        ChatColor.RED + Main._l("lobby.notenoughplayers-rule1")));
              }
            }
          }
          break;
        case SLIME_BALL:
          pie.setCancelled(true);
          g.playerLeave(player, false);
          break;
        case LEATHER_CHESTPLATE:
          pie.setCancelled(true);
          player.updateInventory();
          break;
        default:
          break;
      }
    }
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onExplodeDestroy(EntityExplodeEvent eev) {
    if (eev.isCancelled()) {
      return;
    }

    if (eev.getEntity() == null) {
      return;
    }

    if (eev.getEntity().getWorld() == null) {
      return;
    }

    Game game =
        Main.getInstance().getGameManager().getGameByLocation(eev.getEntity().getLocation());

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.STOPPED) {
      return;
    }

    Iterator<Block> explodeBlocks = eev.blockList().iterator();
    boolean tntDestroyEnabled =
        Main.getInstance().getBooleanConfig("explodes.destroy-worldblocks", false);
    boolean tntDestroyBeds = Main.getInstance().getBooleanConfig("explodes.destroy-beds", false);

    if (!Main.getInstance().getBooleanConfig("explodes.drop-blocks", false)) {
      eev.setYield(0F);
    }

    Material targetMaterial = game.getTargetMaterial();
    while (explodeBlocks.hasNext()) {
      Block exploding = explodeBlocks.next();
      if (!game.getRegion().isInRegion(exploding.getLocation())) {
        explodeBlocks.remove();
        continue;
      }

      if ((!tntDestroyEnabled && !tntDestroyBeds)
          || (!tntDestroyEnabled
              && tntDestroyBeds
              && exploding.getType() != Material.BED_BLOCK
              && exploding.getType() != Material.BED)) {
        if (!game.getRegion().isPlacedBlock(exploding)) {
          if (Main.getInstance().isBreakableType(exploding.getType())) {
            game.getRegion().addBreakedBlock(exploding);
            continue;
          }

          explodeBlocks.remove();
        } else {
          game.getRegion().removePlacedBlock(exploding);
        }

        continue;
      }

      if (game.getRegion().isPlacedBlock(exploding)) {
        game.getRegion().removePlacedBlock(exploding);
        continue;
      }

      if (exploding.getType().equals(targetMaterial)) {
        if (!tntDestroyBeds) {
          explodeBlocks.remove();
          continue;
        }

        // only destroyable by tnt
        if (!eev.getEntityType().equals(EntityType.PRIMED_TNT)
            && !eev.getEntityType().equals(EntityType.MINECART_TNT)) {
          explodeBlocks.remove();
          continue;
        }

        // when it wasn't player who ignited the tnt
        TNTPrimed primedTnt = (TNTPrimed) eev.getEntity();
        if (!(primedTnt.getSource() instanceof Player)) {
          explodeBlocks.remove();
          continue;
        }

        Player p = (Player) primedTnt.getSource();
        if (!game.handleDestroyTargetMaterial(p, exploding)) {
          explodeBlocks.remove();
          continue;
        }
      } else {
        game.getRegion().addBreakedBlock(exploding);
      }
    }
  }
  @EventHandler(priority = EventPriority.HIGH)
  public void onInteractEntity(PlayerInteractEntityEvent event) {
    if (event.getRightClicked() == null) {
      return;
    }

    Entity entity = event.getRightClicked();
    Player player = event.getPlayer();
    if (!player.hasMetadata("bw-addteamjoin")) {
      if (!(entity instanceof LivingEntity)) {
        return;
      }

      LivingEntity livEntity = (LivingEntity) entity;
      Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);
      if (game == null) {
        return;
      }

      if (game.getState() != GameState.WAITING) {
        return;
      }

      Team team = game.getTeam(ChatColor.stripColor(livEntity.getCustomName()));
      if (team == null) {
        return;
      }

      game.playerJoinTeam(player, team);
      event.setCancelled(true);
      return;
    }

    List<MetadataValue> values = player.getMetadata("bw-addteamjoin");
    if (values == null || values.size() == 0) {
      return;
    }

    event.setCancelled(true);
    TeamJoinMetaDataValue value = (TeamJoinMetaDataValue) values.get(0);
    if (!((boolean) value.value())) {
      return;
    }

    if (!(entity instanceof LivingEntity)) {
      player.sendMessage(
          ChatWriter.pluginMessage(ChatColor.RED + Main._l("errors.entitynotcompatible")));
      return;
    }

    LivingEntity living = (LivingEntity) entity;
    living.setRemoveWhenFarAway(false);
    living.setCanPickupItems(false);
    living.setCustomName(value.getTeam().getChatColor() + value.getTeam().getDisplayName());
    living.setCustomNameVisible(
        Main.getInstance().getBooleanConfig("jointeam-entity.show-name", true));

    if (Utils.isSupportingTitles()) {
      if (living.getType().equals(EntityType.valueOf("ARMOR_STAND"))) {
        Utils.equipArmorStand(living, value.getTeam());
      }
    }

    player.removeMetadata("bw-addteamjoin", Main.getInstance());
    player.sendMessage(
        ChatWriter.pluginMessage(
            ChatColor.GREEN
                + Main._l(
                    "success.teamjoinadded",
                    ImmutableMap.of(
                        "team",
                        value.getTeam().getChatColor()
                            + value.getTeam().getDisplayName()
                            + ChatColor.GREEN))));
  }
  @EventHandler
  public void onDamage(EntityDamageEvent ede) {
    if (!(ede.getEntity() instanceof Player)) {
      if (!(ede instanceof EntityDamageByEntityEvent)) {
        return;
      }

      EntityDamageByEntityEvent edbee = (EntityDamageByEntityEvent) ede;
      if (edbee.getDamager() == null || !(edbee.getDamager() instanceof Player)) {
        return;
      }

      Player player = (Player) edbee.getDamager();
      Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

      if (game == null) {
        return;
      }

      if (game.getState() == GameState.WAITING) {
        ede.setCancelled(true);
      }

      return;
    }

    Player p = (Player) ede.getEntity();
    Game g = Main.getInstance().getGameManager().getGameOfPlayer(p);
    if (g == null) {
      return;
    }

    if (g.getState() == GameState.STOPPED) {
      return;
    }

    if (g.getState() == GameState.RUNNING) {
      if (g.isSpectator(p)) {
        ede.setCancelled(true);
        return;
      }

      if (g.isProtected(p) && ede.getCause() != DamageCause.VOID) {
        ede.setCancelled(true);
        return;
      }

      if (Main.getInstance().getBooleanConfig("die-on-void", false)
          && ede.getCause() == DamageCause.VOID) {
        ede.setCancelled(true);
        p.setHealth(0);
        return;
      }

      if (ede instanceof EntityDamageByEntityEvent) {
        EntityDamageByEntityEvent edbee = (EntityDamageByEntityEvent) ede;

        if (edbee.getDamager() instanceof Player) {
          Player damager = (Player) edbee.getDamager();
          if (g.isSpectator(damager)) {
            ede.setCancelled(true);
            return;
          }

          g.setPlayerDamager(p, damager);
        } else if (edbee.getDamager().getType().equals(EntityType.ARROW)) {
          Arrow arrow = (Arrow) edbee.getDamager();
          if (arrow.getShooter() instanceof Player) {
            Player shooter = (Player) arrow.getShooter();
            if (g.isSpectator(shooter)) {
              ede.setCancelled(true);
              return;
            }

            g.setPlayerDamager(p, (Player) arrow.getShooter());
          }
        }
      }

      if (!g.getCycle().isEndGameRunning()) {
        return;
      } else if (ede.getCause() == DamageCause.VOID) {
        p.teleport(g.getPlayerTeam(p).getSpawnLocation());
      }
    } else if (g.getState() == GameState.WAITING) {
      if (ede.getCause() == EntityDamageEvent.DamageCause.VOID) {
        p.teleport(g.getLobby());
      }
    }

    ede.setCancelled(true);
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onChat(AsyncPlayerChatEvent ce) {
    if (ce.isCancelled()) {
      return;
    }

    Player player = ce.getPlayer();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      boolean seperateGameChat = Main.getInstance().getBooleanConfig("seperate-game-chat", true);
      if (!seperateGameChat) {
        return;
      }

      Iterator<Player> recipiens = ce.getRecipients().iterator();
      while (recipiens.hasNext()) {
        Player recipient = recipiens.next();
        Game recipientGame = Main.getInstance().getGameManager().getGameOfPlayer(recipient);
        if (recipientGame != null) {
          recipiens.remove();
        }
      }
      return;
    }

    if (game.getState() == GameState.STOPPED) {
      return;
    }

    Team team = game.getPlayerTeam(player);
    String message = ce.getMessage();
    boolean isSpectator = game.isSpectator(player);

    if (Main.getInstance().getBooleanConfig("overwrite-names", false)) {
      if (team == null || isSpectator) {
        player.setDisplayName(ChatColor.stripColor(player.getName()));

        player.setPlayerListName(ChatColor.stripColor(player.getName()));
      } else {
        player.setDisplayName(team.getChatColor() + ChatColor.stripColor(player.getName()));
        player.setPlayerListName(team.getChatColor() + ChatColor.stripColor(player.getName()));
      }
    }

    if (Main.getInstance().getBooleanConfig("teamname-on-tab", false)
        && Utils.isSupportingTitles()) {
      if (team == null || isSpectator) {
        player.setPlayerListName(ChatColor.stripColor(player.getDisplayName()));
      } else {
        player.setPlayerListName(
            team.getChatColor()
                + team.getName()
                + ChatColor.WHITE
                + " | "
                + team.getChatColor()
                + ChatColor.stripColor(player.getDisplayName()));
      }
    }

    if (game.getState() != GameState.RUNNING && game.getState() == GameState.WAITING) {

      String format = null;
      if (team == null) {
        format =
            this.getChatFormat(
                Main.getInstance().getStringConfig("lobby-chatformat", "$player$: $msg$"),
                null,
                false,
                true);
      } else {
        format =
            this.getChatFormat(
                Main.getInstance().getStringConfig("ingame-chatformat", "<$team$>$player$: $msg$"),
                team,
                false,
                true);
      }

      ce.setFormat(format);
      return;
    }

    String toAllPrefix = Main.getInstance().getConfig().getString("chat-to-all-prefix", "@");

    if (message.trim().startsWith(toAllPrefix)
        || isSpectator
        || (game.getCycle().isEndGameRunning()
            && Main.getInstance().getBooleanConfig("global-chat-after-end", true))) {
      boolean seperateSpectatorChat =
          Main.getInstance().getBooleanConfig("seperate-spectator-chat", false);

      message = message.trim();
      if (!isSpectator
          && !(game.getCycle().isEndGameRunning()
              && Main.getInstance().getBooleanConfig("global-chat-after-end", true))) {
        ce.setMessage(message.substring(1, message.length()));
      } else {
        ce.setMessage(message);
      }

      String format =
          this.getChatFormat(
              Main.getInstance()
                  .getStringConfig("ingame-chatformat-all", "[$all$] <$team$>$player$: $msg$"),
              team,
              isSpectator,
              true);
      ce.setFormat(format);

      if (!Main.getInstance().isBungee() || seperateSpectatorChat) {
        Iterator<Player> recipiens = ce.getRecipients().iterator();
        while (recipiens.hasNext()) {
          Player recipient = recipiens.next();
          if (!game.isInGame(recipient)) {
            recipiens.remove();
            continue;
          }

          if (!seperateSpectatorChat) {
            continue;
          }

          if (isSpectator && !game.isSpectator(recipient)) {
            recipiens.remove();
          } else if (!isSpectator && game.isSpectator(recipient)) {
            recipiens.remove();
          }
        }
      }
    } else {
      message = message.trim();
      ce.setMessage(message);
      ce.setFormat(
          this.getChatFormat(
              Main.getInstance().getStringConfig("ingame-chatformat", "<$team$>$player$: $msg$"),
              team,
              false,
              false));

      Iterator<Player> recipiens = ce.getRecipients().iterator();
      while (recipiens.hasNext()) {
        Player recipient = recipiens.next();
        if (!game.isInGame(recipient) || !team.isInTeam(recipient)) {
          recipiens.remove();
        }
      }
    }
  }
  @SuppressWarnings({"rawtypes", "unchecked"})
  private void onIngameInventoryClick(InventoryClickEvent ice, Player player, Game game) {
    if (!ice.getInventory().getName().equals(Main._l("ingame.shop.name"))) {
      if (game.isSpectator(player)) {
        ItemStack clickedStack = ice.getCurrentItem();
        if (clickedStack == null) {
          return;
        }

        if (ice.getInventory().getName().equals(Main._l("ingame.spectator"))) {
          ice.setCancelled(true);
          if (!clickedStack.getType().equals(Material.SKULL_ITEM)) {
            return;
          }

          SkullMeta meta = (SkullMeta) clickedStack.getItemMeta();
          Player pl = Main.getInstance().getServer().getPlayer(meta.getOwner());
          if (pl == null) {
            return;
          }

          if (!game.isInGame(pl)) {
            return;
          }

          player.teleport(pl);
          player.closeInventory();
          return;
        }

        Material clickedMat = ice.getCurrentItem().getType();
        if (clickedMat.equals(Material.SLIME_BALL)) {
          game.playerLeave(player, false);
        }

        if (clickedMat.equals(Material.COMPASS)) {
          game.openSpectatorCompass(player);
        }
      }
      return;
    }

    ice.setCancelled(true);
    ItemStack clickedStack = ice.getCurrentItem();

    if (clickedStack == null) {
      return;
    }

    if (game.isUsingOldShop(player)) {
      try {
        if (clickedStack.getType() == Material.SNOW_BALL) {
          game.notUseOldShop(player);

          // open new shop
          NewItemShop itemShop = game.openNewItemShop(player);
          itemShop.setCurrentCategory(null);
          itemShop.openCategoryInventory(player);
          return;
        }

        MerchantCategory cat = game.getItemShopCategories().get(clickedStack.getType());
        if (cat == null) {
          return;
        }

        Class clazz =
            Class.forName(
                "io.github.yannici.bedwars.Com."
                    + Main.getInstance().getCurrentVersion()
                    + ".VillagerItemShop");
        Object villagerItemShop =
            clazz
                .getDeclaredConstructor(Game.class, Player.class, MerchantCategory.class)
                .newInstance(game, player, cat);

        Method openTrade = clazz.getDeclaredMethod("openTrading", new Class[] {});
        openTrade.invoke(villagerItemShop, new Object[] {});
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    } else {
      game.getNewItemShop(player).handleInventoryClick(ice, game, player);
    }
  }
  @EventHandler(priority = EventPriority.HIGHEST)
  public void onPlayerDie(PlayerDeathEvent pde) {
    final Player player = pde.getEntity();
    Game game = Main.getInstance().getGameManager().getGameOfPlayer(player);

    if (game == null) {
      return;
    }

    if (game.getState() == GameState.RUNNING) {
      pde.setDroppedExp(0);
      pde.setDeathMessage(null);

      if (!Main.getInstance().getBooleanConfig("player-drops", false)) {
        pde.getDrops().clear();
      }

      try {
        if (!Main.getInstance().isSpigot()) {
          Class<?> clazz = null;
          try {
            clazz =
                Class.forName(
                    "io.github.yannici.bedwars.Com."
                        + Main.getInstance().getCurrentVersion()
                        + ".PerformRespawnRunnable");
          } catch (ClassNotFoundException ex) {
            clazz = Class.forName("io.github.yannici.bedwars.Com.Fallback.PerformRespawnRunnable");
          }

          BukkitRunnable respawnRunnable =
              (BukkitRunnable) clazz.getDeclaredConstructor(Player.class).newInstance(player);
          respawnRunnable.runTaskLater(Main.getInstance(), 20L);
        } else {
          new BukkitRunnable() {

            @Override
            public void run() {
              player.spigot().respawn();
            }
          }.runTaskLater(Main.getInstance(), 20L);
        }

      } catch (Exception e) {
        e.printStackTrace();
      }

      try {
        pde.getClass().getMethod("setKeepInventory", new Class<?>[] {boolean.class});
        pde.setKeepInventory(false);
      } catch (Exception ex) {
        player.getInventory().clear();
      }

      Player killer = player.getKiller();
      if (killer == null) {
        killer = game.getPlayerDamager(player);
      }

      game.getCycle().onPlayerDies(player, killer);
    }
  }