content: /*
   * BedWars1058 - A bed wars mini-game.
   * Copyright (C) 2021 Andrei Dascălu
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <https://www.gnu.org/licenses/>.
   *
   * Contact e-mail: andrew.dascalu@gmail.com
   */
  
  package com.andrei1058.bedwars.support.version.v1_12_R1;
  
  import com.andrei1058.bedwars.api.arena.IArena;
  import com.andrei1058.bedwars.api.arena.shop.ShopHolo;
  import com.andrei1058.bedwars.api.arena.team.ITeam;
  import com.andrei1058.bedwars.api.arena.team.TeamColor;
  import com.andrei1058.bedwars.api.entity.Despawnable;
  import com.andrei1058.bedwars.api.events.player.PlayerKillEvent;
  import com.andrei1058.bedwars.api.exceptions.InvalidEffectException;
  import com.andrei1058.bedwars.api.language.Language;
  import com.andrei1058.bedwars.api.language.Messages;
  import com.andrei1058.bedwars.api.server.VersionSupport;
  import com.andrei1058.bedwars.support.version.common.VersionCommon;
  import net.minecraft.server.v1_12_R1.Item;
  import net.minecraft.server.v1_12_R1.*;
  import org.bukkit.Color;
  import org.bukkit.Location;
  import org.bukkit.Material;
  import org.bukkit.Particle;
  import org.bukkit.block.Bed;
  import org.bukkit.block.BlockState;
  import org.bukkit.command.Command;
  import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
  import org.bukkit.craftbukkit.v1_12_R1.entity.CraftFireball;
  import org.bukkit.craftbukkit.v1_12_R1.entity.CraftLivingEntity;
  import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
  import org.bukkit.craftbukkit.v1_12_R1.entity.CraftTNTPrimed;
  import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack;
  import org.bukkit.entity.*;
  import org.bukkit.event.inventory.InventoryEvent;
  import org.bukkit.inventory.meta.ItemMeta;
  import org.bukkit.inventory.meta.SkullMeta;
  import org.bukkit.plugin.Plugin;
  import org.bukkit.potion.PotionEffectType;
  import org.bukkit.scoreboard.Team;
  import org.bukkit.util.Vector;
  
  import java.lang.reflect.Field;
  import java.util.List;
  import java.util.logging.Level;
  
  @SuppressWarnings("unused")
  public class v1_12_R1 extends VersionSupport {
  
      public v1_12_R1(Plugin plugin, String name) {
          super(plugin, name);
          try {
              setEggBridgeEffect("MOBSPAWNER_FLAMES");
          } catch (InvalidEffectException e) {
              e.printStackTrace();
          }
      }
  
      @Override
      public String getTag(org.bukkit.inventory.ItemStack itemStack, String key) {
          net.minecraft.server.v1_12_R1.ItemStack i = CraftItemStack.asNMSCopy(itemStack);
          if (i == null) {
              return null;
          }
          NBTTagCompound tag = i.getTag();
          return tag == null ? null : tag.hasKey(key) ? tag.getString(key) : null;
      }
  
      @Override
      public void registerVersionListeners() {
          new VersionCommon(this);
      }
  
      @Override
      public void registerCommand(String name, Command clasa) {
          ((CraftServer) getPlugin().getServer()).getCommandMap().register(name, clasa);
      }
  
      @Override
      public void sendTitle(Player p, String title, String subtitle, int fadeIn, int stay, int fadeOut) {
          if (title != null) {
              if (!title.isEmpty()) {
                  IChatBaseComponent bc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + title + "\"}");
                  PacketPlayOutTitle tit = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, bc);
                  PacketPlayOutTitle length = new PacketPlayOutTitle(fadeIn, stay, fadeOut);
                  ((CraftPlayer) p).getHandle().playerConnection.sendPacket(tit);
                  ((CraftPlayer) p).getHandle().playerConnection.sendPacket(length);
              }
          }
          if (subtitle != null) {
              IChatBaseComponent bc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + subtitle + "\"}");
              PacketPlayOutTitle tit = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, bc);
              PacketPlayOutTitle length = new PacketPlayOutTitle(fadeIn, stay, fadeOut);
              ((CraftPlayer) p).getHandle().playerConnection.sendPacket(tit);
              ((CraftPlayer) p).getHandle().playerConnection.sendPacket(length);
          }
      }
  
      public void spawnSilverfish(Location loc, ITeam bedWarsTeam, double speed, double health, int despawn, double damage) {
          new Despawnable(Silverfish.spawn(loc, bedWarsTeam, speed, health, despawn, damage), bedWarsTeam, despawn,
                  Messages.SHOP_UTILITY_NPC_SILVERFISH_NAME, PlayerKillEvent.PlayerKillCause.SILVERFISH_FINAL_KILL, PlayerKillEvent.PlayerKillCause.SILVERFISH);
      }
  
      @Override
      public void spawnIronGolem(Location loc, ITeam bedWarsTeam, double speed, double health, int despawn) {
          new Despawnable(IGolem.spawn(loc, bedWarsTeam, speed, health, despawn), bedWarsTeam, despawn, Messages.SHOP_UTILITY_NPC_IRON_GOLEM_NAME,
                  PlayerKillEvent.PlayerKillCause.IRON_GOLEM_FINAL_KILL, PlayerKillEvent.PlayerKillCause.IRON_GOLEM);
      }
  
      @Override
      public void playAction(Player p, String text) {
          org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer cPlayer = (CraftPlayer) p;
          net.minecraft.server.v1_12_R1.IChatBaseComponent cbc = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + text + "\"}");
          PacketPlayOutChat ppoc = new PacketPlayOutChat(cbc, ChatMessageType.GAME_INFO);
          cPlayer.getHandle().playerConnection.sendPacket(ppoc);
      }
  
      @Override
      public boolean isBukkitCommandRegistered(String name) {
          return ((CraftServer) getPlugin().getServer()).getCommandMap().getCommand(name) != null;
      }
  
      @Override
      public org.bukkit.inventory.ItemStack getItemInHand(Player p) {
          return p.getInventory().getItemInMainHand();
      }
  
      @Override
      public void hideEntity(org.bukkit.entity.Entity e, Player p) {
          PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(e.getEntityId());
          ((CraftPlayer) p).getHandle().playerConnection.sendPacket(packet);
  
      }
  
      @Override
      public void minusAmount(Player p, org.bukkit.inventory.ItemStack i, int amount) {
          if (i.getAmount() - amount <= 0) {
              if(p.getInventory().getItemInOffHand().equals(i)) {
                  p.getInventory().setItemInOffHand(null);
              } else {
                  p.getInventory().removeItem(i);
              }
              return;
          }
          i.setAmount(i.getAmount() - amount);
          p.updateInventory();
      }
  
      @Override
      public void setSource(TNTPrimed tnt, Player owner) {
          EntityLiving nmsEntityLiving = (((CraftLivingEntity) owner).getHandle());
          EntityTNTPrimed nmsTNT = (((CraftTNTPrimed) tnt).getHandle());
          try {
              Field sourceField = EntityTNTPrimed.class.getDeclaredField("source");
              sourceField.setAccessible(true);
              sourceField.set(nmsTNT, nmsEntityLiving);
          } catch (Exception ex) {
              ex.printStackTrace();
          }
      }
  
      @Override
      public boolean isArmor(org.bukkit.inventory.ItemStack itemStack) {
          if (CraftItemStack.asNMSCopy(itemStack) == null) return false;
          if (CraftItemStack.asNMSCopy(itemStack).getItem() == null) return false;
          return CraftItemStack.asNMSCopy(itemStack).getItem() instanceof ItemArmor || CraftItemStack.asNMSCopy(itemStack).getItem() instanceof ItemElytra;
      }
  
      @Override
      public boolean isTool(org.bukkit.inventory.ItemStack itemStack) {
          if (CraftItemStack.asNMSCopy(itemStack) == null) return false;
          if (CraftItemStack.asNMSCopy(itemStack).getItem() == null) return false;
          return CraftItemStack.asNMSCopy(itemStack).getItem() instanceof ItemTool;
      }
  
      @Override
      public boolean isSword(org.bukkit.inventory.ItemStack itemStack) {
          if (CraftItemStack.asNMSCopy(itemStack) == null) return false;
          if (CraftItemStack.asNMSCopy(itemStack).getItem() == null) return false;
          return CraftItemStack.asNMSCopy(itemStack).getItem() instanceof ItemSword;
      }
  
      @Override
      public boolean isAxe(org.bukkit.inventory.ItemStack itemStack) {
          if (CraftItemStack.asNMSCopy(itemStack).getItem() == null) return false;
          return CraftItemStack.asNMSCopy(itemStack).getItem() instanceof ItemAxe;
      }
  
      @Override
      public boolean isBow(org.bukkit.inventory.ItemStack itemStack) {
          if (CraftItemStack.asNMSCopy(itemStack) == null) return false;
          if (CraftItemStack.asNMSCopy(itemStack).getItem() == null) return false;
          return CraftItemStack.asNMSCopy(itemStack).getItem() instanceof ItemBow;
      }
  
      @Override
      public boolean isProjectile(org.bukkit.inventory.ItemStack itemStack) {
          if (CraftItemStack.asNMSCopy(itemStack) == null) return false;
          if (CraftItemStack.asNMSCopy(itemStack).getItem() == null) return false;
          return CraftItemStack.asNMSCopy(itemStack).getItem() instanceof IProjectile;
      }
  
      @Override
      public boolean isInvisibilityPotion(org.bukkit.inventory.ItemStack itemStack) {
          if (!itemStack.getType().equals(org.bukkit.Material.POTION)) return false;
  
          org.bukkit.inventory.meta.PotionMeta pm = (org.bukkit.inventory.meta.PotionMeta) itemStack.getItemMeta();
  
          return pm.hasCustomEffects() && pm.hasCustomEffect(org.bukkit.potion.PotionEffectType.INVISIBILITY);
      }
  
      @Override
      public void registerEntities() {
          registerEntity("Silverfish2", 60, Silverfish.class);
          registerEntity("IGolem", 99, IGolem.class);
      }
  
      @Override
      public void spawnShop(Location loc, String name1, List<Player> players, IArena arena) {
          Location l = loc.clone();
  
          Villager vlg = (Villager) loc.getWorld().spawnEntity(loc, EntityType.VILLAGER);
          vlg.setAI(false);
          vlg.setRemoveWhenFarAway(false);
          vlg.setCollidable(false);
          vlg.setInvulnerable(true);
          vlg.setSilent(true);
  
          for (Player p : players) {
              String[] nume = Language.getMsg(p, name1).split(",");
              if (nume.length == 1) {
                  ArmorStand a = createArmorStand(nume[0], l.clone().add(0, 1.85, 0));
                  new ShopHolo(Language.getPlayerLanguage(p).getIso(), a, null, l, arena);
              } else {
                  ArmorStand a = createArmorStand(nume[0], l.clone().add(0, 2.1, 0));
                  ArmorStand b = createArmorStand(nume[1], l.clone().add(0, 1.85, 0));
                  new ShopHolo(Language.getPlayerLanguage(p).getIso(), a, b, l, arena);
              }
          }
          for (ShopHolo sh : ShopHolo.getShopHolo()) {
              if (sh.getA() == arena) {
                  sh.update();
              }
          }
      }
  
      @Override
      public double getDamage(org.bukkit.inventory.ItemStack i) {
          net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(i);
          NBTTagCompound compound = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound();
          //noinspection ConstantConditions
          return compound.getDouble("generic.attackDamage");
      }
  
      private static ArmorStand createArmorStand(String name, Location loc) {
          ArmorStand a = loc.getWorld().spawn(loc, ArmorStand.class);
          a.setGravity(false);
          a.setVisible(false);
          a.setCustomNameVisible(true);
          a.setCustomName(name);
          return a;
      }
  
  
      @SuppressWarnings({"unchecked", "rawtypes"})
      private void registerEntity(String name, int id, Class customClass) {
          EntityTypes.b.a(id, new MinecraftKey(name), customClass);
      }
  
      /**
       * Spawn shop npc
       */
      private void spawnVillager(Location loc) {
          Villager vlg = (Villager) loc.getWorld().spawnEntity(loc, EntityType.VILLAGER);
          vlg.setAI(false);
          vlg.setRemoveWhenFarAway(false);
          vlg.setCollidable(false);
          vlg.setInvulnerable(true);
          vlg.setSilent(true);
      }
  
      @Override
      public void voidKill(Player p) {
          ((CraftPlayer) p).getHandle().damageEntity(DamageSource.OUT_OF_WORLD, 1000);
      }
  
      @Override
      public void hideArmor(Player victim, Player receiver) {
          PacketPlayOutEntityEquipment helmet = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.HEAD, new ItemStack(Item.getById(0)));
          PacketPlayOutEntityEquipment chest = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.CHEST, new ItemStack(Item.getById(0)));
          PacketPlayOutEntityEquipment pants = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.LEGS, new ItemStack(Item.getById(0)));
          PacketPlayOutEntityEquipment boots = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.FEET, new ItemStack(Item.getById(0)));
          EntityPlayer pc = ((CraftPlayer) receiver).getHandle();
          pc.playerConnection.sendPacket(helmet);
          pc.playerConnection.sendPacket(chest);
          pc.playerConnection.sendPacket(pants);
          pc.playerConnection.sendPacket(boots);
      }
  
      @Override
      public void showArmor(Player victim, Player receiver) {
          PacketPlayOutEntityEquipment helmet = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.HEAD, CraftItemStack.asNMSCopy(victim.getInventory().getHelmet()));
          PacketPlayOutEntityEquipment chest = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.CHEST, CraftItemStack.asNMSCopy(victim.getInventory().getChestplate()));
          PacketPlayOutEntityEquipment pants = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.LEGS, CraftItemStack.asNMSCopy(victim.getInventory().getLeggings()));
          PacketPlayOutEntityEquipment boots = new PacketPlayOutEntityEquipment(victim.getEntityId(), EnumItemSlot.FEET, CraftItemStack.asNMSCopy(victim.getInventory().getBoots()));
          EntityPlayer pc = ((CraftPlayer) receiver).getHandle();
          pc.playerConnection.sendPacket(helmet);
          pc.playerConnection.sendPacket(chest);
          pc.playerConnection.sendPacket(pants);
          pc.playerConnection.sendPacket(boots);
      }
  
      @Override
      public void spawnDragon(Location l, ITeam bwt) {
          if (l == null || l.getWorld() == null) {
              getPlugin().getLogger().log(Level.WARNING, "Could not spawn Dragon. Location is null");
              return;
          }
          EnderDragon ed = (EnderDragon) l.getWorld().spawnEntity(l, EntityType.ENDER_DRAGON);
          ed.setPhase(EnderDragon.Phase.CIRCLING);
      }
  
      @Override
      public void colorBed(ITeam bwt) {
          for (int x = -1; x <= 1; x++) {
              for (int z = -1; z <= 1; z++) {
                  BlockState bed = bwt.getBed().clone().add(x, 0, z).getBlock().getState();
                  if (bed instanceof Bed) {
                      ((Bed) bed).setColor(bwt.getColor().dye());
                      bed.update();
                  }
              }
          }
      }
  
      @Override
      public void registerTntWhitelist(float endStoneBlast, float glassBlast) {
          try {
              Field field = Block.class.getDeclaredField("durability");
              field.setAccessible(true);
              field.set(Block.getByName("glass"), glassBlast);
              field.set(Block.getByName("stained_glass"), glassBlast);
              field.set(Block.getByName("end_stone"), endStoneBlast);
          } catch (NoSuchFieldException | IllegalAccessException e) {
              e.printStackTrace();
          }
      }
  
      @Override
      @SuppressWarnings("deprecation")
      public void setBlockTeamColor(org.bukkit.block.Block block, TeamColor teamColor) {
          block.setData(teamColor.itemByte());
      }
  
      @Override
      public void setCollide(Player p, IArena a, boolean value) {
          p.setCollidable(value);
          if (a == null) return;
          a.updateSpectatorCollideRule(p, value);
      }
  
      @Override
      public org.bukkit.inventory.ItemStack addCustomData(org.bukkit.inventory.ItemStack i, String data) {
          ItemStack itemStack = CraftItemStack.asNMSCopy(i);
          NBTTagCompound tag = itemStack.getTag();
          if (tag == null) {
              tag = new NBTTagCompound();
          }
  
          tag.setString("BedWars1058", data);
          itemStack.setTag(tag);
          return CraftItemStack.asBukkitCopy(itemStack);
      }
  
      @Override
      public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack i, String key, String value) {
          ItemStack itemStack = CraftItemStack.asNMSCopy(i);
          NBTTagCompound tag = itemStack.getTag();
          if (tag == null) {
              tag = new NBTTagCompound();
          }
  
          tag.setString(key, value);
          itemStack.setTag(tag);
          return CraftItemStack.asBukkitCopy(itemStack);
      }
  
      @Override
      public boolean isCustomBedWarsItem(org.bukkit.inventory.ItemStack i) {
          ItemStack itemStack = CraftItemStack.asNMSCopy(i);
          NBTTagCompound tag = itemStack.getTag();
          if (tag == null) return false;
          return tag.hasKey("BedWars1058");
      }
  
      @Override
      public String getCustomData(org.bukkit.inventory.ItemStack i) {
          ItemStack itemStack = CraftItemStack.asNMSCopy(i);
          NBTTagCompound tag = itemStack.getTag();
          if (tag == null) return "";
          return tag.getString("BedWars1058");
      }
  
      @Override
      public org.bukkit.inventory.ItemStack colourItem(org.bukkit.inventory.ItemStack itemStack, ITeam bedWarsTeam) {
          if (itemStack == null) return null;
          switch (itemStack.getType().toString()) {
              default:
                  return itemStack;
              case "WOOL":
              case "STAINED_CLAY":
              case "STAINED_GLASS":
                  return new org.bukkit.inventory.ItemStack(itemStack.getType(), itemStack.getAmount(), bedWarsTeam.getColor().itemByte());
              case "GLASS":
                  return new org.bukkit.inventory.ItemStack(org.bukkit.Material.STAINED_GLASS, itemStack.getAmount(), bedWarsTeam.getColor().itemByte());
          }
      }
  
      @Override
      public org.bukkit.inventory.ItemStack createItemStack(String material, int amount, short data) {
          org.bukkit.inventory.ItemStack i;
          try {
              i = new org.bukkit.inventory.ItemStack(org.bukkit.Material.valueOf(material), amount, data);
          } catch (Exception ex) {
              getPlugin().getLogger().severe(material + " is not a valid " + getVersion() + " material!");
              i = new org.bukkit.inventory.ItemStack(org.bukkit.Material.BEDROCK);
          }
          return i;
      }
  
      @Override
      public boolean isPlayerHead(String material, int data) {
          return material.equals("SKULL_ITEM") && data == 3;
      }
  
      @Override
      public org.bukkit.Material materialFireball() {
          return org.bukkit.Material.FIREBALL;
      }
  
      @Override
      public org.bukkit.Material materialPlayerHead() {
          return org.bukkit.Material.SKULL_ITEM;
      }
  
      @Override
      public org.bukkit.Material materialSnowball() {
          return org.bukkit.Material.SNOW_BALL;
      }
  
      @Override
      public org.bukkit.Material materialGoldenHelmet() {
          return org.bukkit.Material.GOLD_HELMET;
      }
  
      @Override
      public org.bukkit.Material materialGoldenChestPlate() {
          return org.bukkit.Material.GOLD_CHESTPLATE;
      }
  
      @Override
      public org.bukkit.Material materialGoldenLeggings() {
          return org.bukkit.Material.GOLD_LEGGINGS;
      }
  
      @Override
      public org.bukkit.Material materialNetheriteHelmet() {
          return Material.DIAMOND_HELMET; //Netherite doesn't exist
      }
  
      @Override
      public org.bukkit.Material materialNetheriteChestPlate() {
          return Material.DIAMOND_CHESTPLATE; //Netherite doesn't exist
      }
  
      @Override
      public org.bukkit.Material materialNetheriteLeggings() {
          return Material.DIAMOND_LEGGINGS; //Netherite doesn't exist
      }
  
      @Override
      public org.bukkit.Material materialElytra() {
          return Material.ELYTRA;
      }
  
      @Override
      public org.bukkit.Material materialCake() {
          return org.bukkit.Material.CAKE_BLOCK;
      }
  
      @Override
      public org.bukkit.Material materialCraftingTable() {
          return org.bukkit.Material.WORKBENCH;
      }
  
      @Override
      public org.bukkit.Material materialEnchantingTable() {
          return org.bukkit.Material.ENCHANTMENT_TABLE;
      }
  
      @Override
      public boolean isBed(org.bukkit.Material material) {
          return material == org.bukkit.Material.BED_BLOCK || material == org.bukkit.Material.BED;
      }
  
      @SuppressWarnings("deprecation")
      @Override
      public boolean itemStackDataCompare(org.bukkit.inventory.ItemStack i, short data) {
          return i.getData().getData() == data;
      }
  
      @SuppressWarnings("deprecation")
      @Override
      public void setJoinSignBackgroundBlockData(BlockState block, byte data) {
          block.getBlock().getRelative(((org.bukkit.material.Sign) block.getData()).getAttachedFace()).setData(data, true);
      }
  
      @Override
      public org.bukkit.Material woolMaterial() {
          return org.bukkit.Material.WOOL;
      }
  
      @Override
      public String getShopUpgradeIdentifier(org.bukkit.inventory.ItemStack itemStack) {
          ItemStack i = CraftItemStack.asNMSCopy(itemStack);
          NBTTagCompound tag = i.getTag();
          return tag == null ? "" : tag.hasKey("tierIdentifier") ? tag.getString("tierIdentifier") : "";
      }
  
      @Override
      public org.bukkit.inventory.ItemStack setShopUpgradeIdentifier(org.bukkit.inventory.ItemStack itemStack, String identifier) {
          ItemStack i = CraftItemStack.asNMSCopy(itemStack);
          NBTTagCompound tag = i.getTag();
          if (tag == null) {
              tag = new NBTTagCompound();
          }
  
          tag.setString(VersionSupport.PLUGIN_TAG_TIER_KEY, identifier);
          i.setTag(tag);
          return CraftItemStack.asBukkitCopy(i);
      }
  
      @Override
      public org.bukkit.inventory.ItemStack getPlayerHead(Player player, org.bukkit.inventory.ItemStack copyTagFrom) {
          org.bukkit.inventory.ItemStack head = new org.bukkit.inventory.ItemStack(org.bukkit.Material.SKULL_ITEM, 1, (short) 3);
  
          if (copyTagFrom != null) {
              ItemStack i = CraftItemStack.asNMSCopy(head);
              i.setTag(CraftItemStack.asNMSCopy(copyTagFrom).getTag());
              head = CraftItemStack.asBukkitCopy(i);
          }
  
          SkullMeta headMeta = (SkullMeta) head.getItemMeta();
          Field profileField;
          try {
              profileField = headMeta.getClass().getDeclaredField("profile");
              profileField.setAccessible(true);
              profileField.set(headMeta, ((CraftPlayer) player).getProfile());
          } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e1) {
              e1.printStackTrace();
          }
          head.setItemMeta(headMeta);
          return head;
      }
  
      @Override
      public void sendPlayerSpawnPackets(Player respawned, IArena arena) {
          if (respawned == null) return;
          if (arena == null) return;
          if (!arena.isPlayer(respawned)) return;
  
          // if method was used when the player was still in re-spawning screen
          if (arena.isReSpawning(respawned)) return;
  
          EntityPlayer entityPlayer = ((CraftPlayer) respawned).getHandle();
          PacketPlayOutNamedEntitySpawn show = new PacketPlayOutNamedEntitySpawn(entityPlayer);
          PacketPlayOutEntityVelocity playerVelocity = new PacketPlayOutEntityVelocity(entityPlayer);
          PacketPlayOutEntityHeadRotation head = new PacketPlayOutEntityHeadRotation(entityPlayer, getCompressedAngle(entityPlayer.yaw));
  
          PacketPlayOutEntityEquipment hand1 = new PacketPlayOutEntityEquipment(entityPlayer.getId(), EnumItemSlot.MAINHAND, entityPlayer.inventory.getItemInHand());
          PacketPlayOutEntityEquipment hand2 = new PacketPlayOutEntityEquipment(entityPlayer.getId(), EnumItemSlot.OFFHAND, entityPlayer.getItemInOffHand());
          PacketPlayOutEntityEquipment helmet = new PacketPlayOutEntityEquipment(entityPlayer.getId(), EnumItemSlot.HEAD, entityPlayer.inventory.getArmorContents().get(3));
          PacketPlayOutEntityEquipment chest = new PacketPlayOutEntityEquipment(entityPlayer.getId(), EnumItemSlot.CHEST, entityPlayer.inventory.getArmorContents().get(2));
          PacketPlayOutEntityEquipment pants = new PacketPlayOutEntityEquipment(entityPlayer.getId(), EnumItemSlot.LEGS, entityPlayer.inventory.getArmorContents().get(1));
          PacketPlayOutEntityEquipment boots = new PacketPlayOutEntityEquipment(entityPlayer.getId(), EnumItemSlot.FEET, entityPlayer.inventory.getArmorContents().get(0));
  
          for (Player p : arena.getPlayers()) {
              if (p == null) continue;
              if (p.equals(respawned)) continue;
              // if p is in re-spawning screen continue
              if (arena.isReSpawning(p)) continue;
  
              EntityPlayer boundTo = ((CraftPlayer) p).getHandle();
              if (p.getWorld().equals(respawned.getWorld())) {
                  if (respawned.getLocation().distance(p.getLocation()) <= arena.getRenderDistance()) {
  
                      // send respawned player to regular players
                      boundTo.playerConnection.sendPacket(show);
                      boundTo.playerConnection.sendPacket(playerVelocity);
                      for (Packet<?> packet : new Packet[]{hand1, helmet, chest, pants, boots, hand2, head}) {
                          boundTo.playerConnection.sendPacket(packet);
                      }
  
                      // send nearby players to respawned player
                      // if the player has invisibility hide armor
                      if (p.hasPotionEffect(PotionEffectType.INVISIBILITY)) {
                          hideArmor(p, respawned);
                      } else {
                          PacketPlayOutNamedEntitySpawn show2 = new PacketPlayOutNamedEntitySpawn(boundTo);
                          PacketPlayOutEntityVelocity playerVelocity2 = new PacketPlayOutEntityVelocity(boundTo);
                          PacketPlayOutEntityHeadRotation head2 = new PacketPlayOutEntityHeadRotation(boundTo, getCompressedAngle(boundTo.yaw));
                          entityPlayer.playerConnection.sendPacket(show2);
                          entityPlayer.playerConnection.sendPacket(playerVelocity2);
                          entityPlayer.playerConnection.sendPacket(head2);
                          showArmor(p, respawned);
                      }
                  }
              }
          }
  
          for (Player spectator : arena.getSpectators()) {
              if (spectator == null) continue;
              if (spectator.equals(respawned)) continue;
              EntityPlayer boundTo = ((CraftPlayer) spectator).getHandle();
              respawned.hidePlayer(getPlugin(), spectator);
              if (spectator.getWorld().equals(respawned.getWorld())) {
                  if (respawned.getLocation().distance(spectator.getLocation()) <= arena.getRenderDistance()) {
  
                      // send respawned player to spectator
                      boundTo.playerConnection.sendPacket(show);
                      boundTo.playerConnection.sendPacket(playerVelocity);
                      boundTo.playerConnection.sendPacket(new PacketPlayOutEntityHeadRotation(entityPlayer, getCompressedAngle(entityPlayer.yaw)));
                      for (Packet<?> packet : new Packet[]{hand1, helmet, chest, pants, boots, hand2}) {
                          boundTo.playerConnection.sendPacket(packet);
                      }
                  }
              }
          }
      }
  
      @Override
      public String getInventoryName(InventoryEvent e) {
          return e.getInventory().getName();
      }
  
      @Override
      public void setUnbreakable(ItemMeta itemMeta) {
          itemMeta.setUnbreakable(true);
      }
  
      @Override
      public String getMainLevel() {
          //noinspection deprecation
          return ((DedicatedServer) MinecraftServer.getServer()).propertyManager.properties.getProperty("level-name");
      }
  
      @Override
      public int getVersion() {
          return 5;
      }
  
      @Override
      public void setJoinSignBackground(org.bukkit.block.BlockState b, org.bukkit.Material material) {
          b.getLocation().getBlock().getRelative(((org.bukkit.material.Sign) b.getData()).getAttachedFace()).setType(material);
      }
  
      @Override
      public void spigotShowPlayer(Player victim, Player receiver) {
          receiver.showPlayer(getPlugin(), victim);
      }
  
      @Override
      public void spigotHidePlayer(Player victim, Player receiver) {
          receiver.hidePlayer(getPlugin(), victim);
      }
  
      @Override
      public Fireball setFireballDirection(Fireball fireball, Vector vector) {
          EntityFireball fb = ((CraftFireball) fireball).getHandle();
          fb.dirX = vector.getX() * 0.1D;
          fb.dirY = vector.getY() * 0.1D;
          fb.dirZ = vector.getZ() * 0.1D;
          return (Fireball) fb.getBukkitEntity();
      }
  
      @Override
      public void playRedStoneDot(Player player) {
          Color color = Color.RED;
          PacketPlayOutWorldParticles particlePacket = new PacketPlayOutWorldParticles(EnumParticle.REDSTONE, true, (float) player.getLocation().getX(),
                  (float) (player.getLocation().getY() + 2.6), (float) player.getLocation().getZ(), color.getRed(), color.getRed(), color.getRed(), 0, 0);
          for (Player inWorld : player.getWorld().getPlayers()) {
              if (inWorld.equals(player)) continue;
              ((CraftPlayer) inWorld).getHandle().playerConnection.sendPacket(particlePacket);
          }
      }
  
      @Override
      public void clearArrowsFromPlayerBody(Player player) {
          ((CraftLivingEntity)player).getHandle().getDataWatcher().set(new DataWatcherObject<>(10, DataWatcherRegistry.b),-1);
      }
  
      @Override
      public void placeTowerBlocks(org.bukkit.block.Block b, IArena a, TeamColor color, int x, int y, int z){
          b.getRelative(x, y, z).setType(Material.WOOL);
          setBlockTeamColor(b.getRelative(x, y, z), color);
          a.addPlacedBlock(b.getRelative(x, y, z));
      }
  
      @Override
      public void placeLadder(org.bukkit.block.Block b, int x, int y, int z, IArena a, int ladderdata){
          b.getRelative(x, y, z).setType(Material.LADDER);
          //noinspection deprecation
          b.getRelative(x, y, z).setData((byte)ladderdata);
          a.addPlacedBlock(b.getRelative(x, y, z));
      }
  
      @Override
      public void playVillagerEffect(Player player, Location location){
          player.spawnParticle(Particle.VILLAGER_HAPPY, location, 1);
      }
  
  }
  