diff --git a/bedwars-api/src/main/java/com/andrei1058/bedwars/api/server/VersionSupport.java b/bedwars-api/src/main/java/com/andrei1058/bedwars/api/server/VersionSupport.java index 873ef2fe9..07d94932d 100644 --- a/bedwars-api/src/main/java/com/andrei1058/bedwars/api/server/VersionSupport.java +++ b/bedwars-api/src/main/java/com/andrei1058/bedwars/api/server/VersionSupport.java @@ -54,6 +54,10 @@ public abstract class VersionSupport { private static final ConcurrentHashMap despawnables = new ConcurrentHashMap<>(); private final Plugin plugin; + /** + * @param plugin bed-wars instance. + * @param versionName version name. + */ public VersionSupport(Plugin plugin, String versionName) { name2 = versionName; this.plugin = plugin; diff --git a/pom.xml b/pom.xml index 005233d2e..ab55b0564 100644 --- a/pom.xml +++ b/pom.xml @@ -115,6 +115,8 @@ versionsupport_v1_20_R2 versionsupport_v1_20_R3 versionsupport_v1_20_R4 + versionsupport_v1_21_R1 + versionsupport_nms_cmn1 diff --git a/versionsupport_nms_cmn1/pom.xml b/versionsupport_nms_cmn1/pom.xml new file mode 100644 index 000000000..3350ff5b0 --- /dev/null +++ b/versionsupport_nms_cmn1/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + com.andrei1058.bedwars + BedWars1058 + 23.12.1-SNAPSHOT + + + versionsupport_nms_cmn1 + + + 21 + 21 + UTF-8 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + + + + + NMS common code for versions where server version was removed from package name. + This is known to be compatible with 1.21 + + + + + org.spigotmc + spigot + 1.21.1-R0.1-SNAPSHOT + jar + provided + + + commons-codec + commons-codec + + + + + com.andrei1058.bedwars + bedwars-api + ${project.version} + jar + provided + + + com.andrei1058.bedwars + versionsupport-common + ${project.version} + compile + + + + \ No newline at end of file diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/AbstractVerImplCmn1.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/AbstractVerImplCmn1.java new file mode 100644 index 000000000..ba659062a --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/AbstractVerImplCmn1.java @@ -0,0 +1,664 @@ +package dev.andrei1058.mc.bedwars; + +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.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 com.mojang.datafixers.util.Pair; +import dev.andrei1058.mc.bedwars.despawnable.DespawnableAttributes; +import dev.andrei1058.mc.bedwars.despawnable.DespawnableFactory; +import dev.andrei1058.mc.bedwars.despawnable.DespawnableType; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import net.minecraft.core.particles.ParticleParamRedstone; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.*; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.world.entity.EnumItemSlot; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockBase; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.type.Bed; +import org.bukkit.block.data.type.Ladder; +import org.bukkit.block.data.type.WallSign; +import org.bukkit.command.Command; +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.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; + +/** + * Common NMS code for versions: 1.21. + * This was introduced because nms package is now free of version code. + */ +public abstract class AbstractVerImplCmn1 extends VersionSupport { + + private final DespawnableFactory despawnableFactory; + + public AbstractVerImplCmn1(Plugin plugin, String name) { + super(plugin, name); + loadDefaultEffects(); + this.despawnableFactory = new DespawnableFactory(this); + } + + @Override + public void registerVersionListeners() { + new VersionCommon(this); + } + + abstract public void registerCommand(String name, Command cmd); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key) { + var tag = getTag(itemStack); + return tag == null ? null : tag.e(key) ? tag.l(key) : null; + } + + public abstract @Nullable NBTTagCompound getTag(@NotNull org.bukkit.inventory.ItemStack itemStack); + + @Override + public void sendTitle(@NotNull Player p, String title, String subtitle, int fadeIn, int stay, int fadeOut) { + p.sendTitle(title == null ? " " : title, subtitle == null ? " " : subtitle, fadeIn, stay, fadeOut); + } + + public void spawnSilverfish(Location loc, ITeam bedWarsTeam, double speed, double health, int despawn, double damage) { + var attr = new DespawnableAttributes(DespawnableType.SILVERFISH, speed, health, damage, despawn); + var entity = despawnableFactory.spawn(attr, loc, bedWarsTeam); + + new Despawnable( + entity, + 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) { + var attr = new DespawnableAttributes(DespawnableType.IRON_GOLEM, speed, health, 4, despawn); + var entity = despawnableFactory.spawn(attr, loc, bedWarsTeam); + new Despawnable( + entity, + bedWarsTeam, despawn, + Messages.SHOP_UTILITY_NPC_IRON_GOLEM_NAME, + PlayerKillEvent.PlayerKillCause.IRON_GOLEM_FINAL_KILL, + PlayerKillEvent.PlayerKillCause.IRON_GOLEM + ); + } + + @Override + public void playAction(@NotNull Player p, String text) { + p.spigot().sendMessage( + ChatMessageType.ACTION_BAR, + new TextComponent(ChatColor.translateAlternateColorCodes('&', text) + ) + ); + } + + @Override + public org.bukkit.inventory.ItemStack getItemInHand(@NotNull Player p) { + return p.getInventory().getItemInMainHand(); + } + + @Override + public void hideEntity(@NotNull Entity e, Player p) { + PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(e.getEntityId()); + this.sendPacket(p, packet); + } + + @Override + public void minusAmount(Player p, org.bukkit.inventory.@NotNull 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); + + // todo this is marked as unstable on 1.20 for some reason + p.updateInventory(); + } + + public abstract boolean isBukkitCommandRegistered(String name); + + public abstract void setSource(TNTPrimed tnt, Player owner); + + @Override + public boolean isInvisibilityPotion(org.bukkit.inventory.@NotNull 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 != null && pm.hasCustomEffects() && pm.hasCustomEffect(org.bukkit.potion.PotionEffectType.INVISIBILITY); + } + + @Override + public void registerEntities() { + } + + @Override + public void spawnShop(@NotNull Location loc, String name1, List players, IArena arena) { + Location l = loc.clone(); + + if (l.getWorld() == null) return; + Villager vlg = (Villager) l.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[] name = Language.getMsg(p, name1).split(","); + if (name.length == 1) { + ArmorStand a = createArmorStand(name[0], l.clone().add(0, 1.85, 0)); + new ShopHolo(Language.getPlayerLanguage(p).getIso(), a, null, l, arena); + } else { + ArmorStand a = createArmorStand(name[0], l.clone().add(0, 2.1, 0)); + ArmorStand b = createArmorStand(name[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) { + var tag = getTag(i); + if (null == tag) { + throw new RuntimeException("Provided item has no Tag"); + } + return tag.k("generic.attackDamage"); + } + + private static ArmorStand createArmorStand(String name, Location loc) { + if (loc == null) return null; + if (loc.getWorld() == null) return null; + ArmorStand a = loc.getWorld().spawn(loc, ArmorStand.class); + a.setGravity(false); + a.setVisible(false); + a.setCustomNameVisible(true); + a.setCustomName(name); + return a; + } + + @Override + public void hideArmor(@NotNull Player victim, Player receiver) { + List> items = new ArrayList<>(); + items.add(new Pair<>(EnumItemSlot.f, new ItemStack(net.minecraft.world.item.Item.b(0)))); + items.add(new Pair<>(EnumItemSlot.e, new ItemStack(net.minecraft.world.item.Item.b(0)))); + items.add(new Pair<>(EnumItemSlot.d, new ItemStack(net.minecraft.world.item.Item.b(0)))); + items.add(new Pair<>(EnumItemSlot.c, new ItemStack(Item.b(0)))); + PacketPlayOutEntityEquipment packet1 = new PacketPlayOutEntityEquipment(victim.getEntityId(), items); + sendPacket(receiver, packet1); + } + + @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.setType(bwt.getColor().bedMaterial()); + bed.update(); + } + } + } + } + + @Override + public void registerTntWhitelist(float endStoneBlast, float glassBlast) { + try { + // blast resistance + Field field = BlockBase.class.getDeclaredField("aH"); + field.setAccessible(true); + // end stone + field.set(Blocks.fz, endStoneBlast); + // obsidian +// field.set(Blocks.co, glassBlast); + // standard glass + field.set(Blocks.aQ, glassBlast); + + var coloredGlass = new net.minecraft.world.level.block.Block[]{ + Blocks.ej, Blocks.ek, Blocks.el, Blocks.em, + Blocks.en, Blocks.eo, Blocks.ep, Blocks.eq, + Blocks.er, Blocks.es, Blocks.et, Blocks.eu, + Blocks.ev, Blocks.ew, Blocks.ex, Blocks.ey, + + // tinted glass + Blocks.qB, + }; + + Arrays.stream(coloredGlass).forEach( + glass -> { + try { + field.set(glass, glassBlast); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + ); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void setBlockTeamColor(@NotNull Block block, TeamColor teamColor) { + if (block.getType().toString().contains("STAINED_GLASS") || block.getType().toString().equals("GLASS")) { + block.setType(teamColor.glassMaterial()); + } else if (block.getType().toString().contains("_TERRACOTTA")) { + block.setType(teamColor.glazedTerracottaMaterial()); + } else if (block.getType().toString().contains("_WOOL")) { + block.setType(teamColor.woolMaterial()); + } + } + + @Override + public void setCollide(@NotNull 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) { + var tag = getCreateTag(i); + tag.a(VersionSupport.PLUGIN_TAG_GENERIC_KEY, data); + return applyTag(i, tag); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + var tag = getCreateTag(itemStack); + tag.a(key, value); + return applyTag(itemStack, tag); + } + + @Override + public boolean isCustomBedWarsItem(org.bukkit.inventory.ItemStack i) { + return getCreateTag(i).e(VersionSupport.PLUGIN_TAG_GENERIC_KEY); + } + + @Override + public String getCustomData(org.bukkit.inventory.ItemStack i) { + return getCreateTag(i).l(VersionSupport.PLUGIN_TAG_GENERIC_KEY); + } + + @Override + public org.bukkit.inventory.ItemStack colourItem(org.bukkit.inventory.ItemStack itemStack, ITeam bedWarsTeam) { + if (itemStack == null) return null; + String type = itemStack.getType().toString(); + if (isBed(itemStack.getType())) { + return new org.bukkit.inventory.ItemStack(bedWarsTeam.getColor().bedMaterial(), itemStack.getAmount()); + } else if (type.contains("_STAINED_GLASS_PANE")) { + return new org.bukkit.inventory.ItemStack(bedWarsTeam.getColor().glassPaneMaterial(), itemStack.getAmount()); + } else if (type.contains("STAINED_GLASS") || type.equals("GLASS")) { + return new org.bukkit.inventory.ItemStack(bedWarsTeam.getColor().glassMaterial(), itemStack.getAmount()); + } else if (type.contains("_TERRACOTTA")) { + return new org.bukkit.inventory.ItemStack(bedWarsTeam.getColor().glazedTerracottaMaterial(), itemStack.getAmount()); + } else if (type.contains("_WOOL")) { + return new org.bukkit.inventory.ItemStack(bedWarsTeam.getColor().woolMaterial(), itemStack.getAmount()); + } + return itemStack; + } + + @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); + } catch (Exception ex) { + getPlugin().getLogger().log(Level.WARNING, material + " is not a valid " + getName() + " material!"); + i = new org.bukkit.inventory.ItemStack(org.bukkit.Material.BEDROCK); + } + return i; + } + + + @Override + public org.bukkit.Material materialFireball() { + return org.bukkit.Material.FIRE_CHARGE; + } + + @Override + public org.bukkit.Material materialPlayerHead() { + return org.bukkit.Material.PLAYER_HEAD; + } + + @Override + public org.bukkit.Material materialSnowball() { + return org.bukkit.Material.SNOWBALL; + } + + @Override + public org.bukkit.Material materialGoldenHelmet() { + return org.bukkit.Material.GOLDEN_HELMET; + } + + @Override + public org.bukkit.Material materialGoldenChestPlate() { + return org.bukkit.Material.GOLDEN_CHESTPLATE; + } + + @Override + public org.bukkit.Material materialGoldenLeggings() { + return org.bukkit.Material.GOLDEN_LEGGINGS; + } + + @Override + public org.bukkit.Material materialNetheriteHelmet() { + return Material.NETHERITE_HELMET; + } + + @Override + public org.bukkit.Material materialNetheriteChestPlate() { + return Material.NETHERITE_CHESTPLATE; + } + + @Override + public org.bukkit.Material materialNetheriteLeggings() { + return Material.NETHERITE_LEGGINGS; + } + + @Override + public org.bukkit.Material materialElytra() { + return Material.ELYTRA; + } + + @Override + public org.bukkit.Material materialCake() { + return org.bukkit.Material.CAKE; + } + + @Override + public org.bukkit.Material materialCraftingTable() { + return org.bukkit.Material.CRAFTING_TABLE; + } + + @Override + public org.bukkit.Material materialEnchantingTable() { + return org.bukkit.Material.ENCHANTING_TABLE; + } + + @Override + public org.bukkit.Material woolMaterial() { + return org.bukkit.Material.WHITE_WOOL; + } + + + @Override + public String getShopUpgradeIdentifier(org.bukkit.inventory.ItemStack itemStack) { + var tag = getCreateTag(itemStack); + return tag.e(VersionSupport.PLUGIN_TAG_TIER_KEY) ? tag.l(VersionSupport.PLUGIN_TAG_TIER_KEY) : "null"; + } + + @Override + public org.bukkit.inventory.ItemStack setShopUpgradeIdentifier(org.bukkit.inventory.ItemStack itemStack, String identifier) { + var tag = getCreateTag(itemStack); + tag.a(VersionSupport.PLUGIN_TAG_TIER_KEY, identifier); + return applyTag(itemStack, tag); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerHead(Player player, org.bukkit.inventory.ItemStack copyTagFrom) { + org.bukkit.inventory.ItemStack head = new org.bukkit.inventory.ItemStack(materialPlayerHead()); + + if (copyTagFrom != null) { + var tag = getTag(copyTagFrom); + head = applyTag(head, tag); + } + + var meta = head.getItemMeta(); + if (meta instanceof SkullMeta) { + ((SkullMeta) meta).setOwnerProfile(player.getPlayerProfile()); + } + head.setItemMeta(meta); + 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.getRespawnSessions().containsKey(respawned)) return; + + EntityPlayer entityPlayer = getPlayer(respawned); + PacketPlayOutSpawnEntity show = new PacketPlayOutSpawnEntity(entityPlayer); + PacketPlayOutEntityVelocity playerVelocity = new PacketPlayOutEntityVelocity(entityPlayer); + // we send head rotation packet because sometimes on respawn others see him with bad rotation + PacketPlayOutEntityHeadRotation head = new PacketPlayOutEntityHeadRotation(entityPlayer, getCompressedAngle(entityPlayer.getBukkitYaw())); + + // retrieve current armor and in-hand items + // we send a packet later for timing issues where other players do not see them + List> list = getPlayerEquipment(entityPlayer); + + + for (Player p : arena.getPlayers()) { + if (p == null) continue; + if (p.equals(respawned)) continue; + // if p is in re-spawning screen continue + if (arena.getRespawnSessions().containsKey(p)) continue; + + EntityPlayer boundTo = getPlayer(p); + if (p.getWorld().equals(respawned.getWorld())) { + if (respawned.getLocation().distance(p.getLocation()) <= arena.getRenderDistance()) { + + // send respawned player to regular players + this.sendPackets( + p, show, head, playerVelocity, + new PacketPlayOutEntityEquipment(respawned.getEntityId(), list) + ); + + // send nearby players to respawned player + // if the player has invisibility hide armor + if (p.hasPotionEffect(PotionEffectType.INVISIBILITY)) { + hideArmor(p, respawned); + } else { + + PacketPlayOutSpawnEntity show2 = new PacketPlayOutSpawnEntity(boundTo); + PacketPlayOutEntityVelocity playerVelocity2 = new PacketPlayOutEntityVelocity(boundTo); + PacketPlayOutEntityHeadRotation head2 = new PacketPlayOutEntityHeadRotation(boundTo, getCompressedAngle(boundTo.getBukkitYaw())); + this.sendPackets(respawned, show2, playerVelocity2, 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 + this.sendPackets( + spectator, show, playerVelocity, + new PacketPlayOutEntityEquipment(respawned.getEntityId(), list), + new PacketPlayOutEntityHeadRotation(entityPlayer, getCompressedAngle(entityPlayer.getBukkitYaw())) + ); + } + } + } + } + + @Override + public String getInventoryName(@NotNull InventoryEvent e) { + return e.getView().getTitle(); + } + + @Override + public void setUnbreakable(@NotNull ItemMeta itemMeta) { + itemMeta.setUnbreakable(true); + } + + @Override + public abstract String getMainLevel(); + @Override + abstract public int getVersion(); + + @Override + public void setJoinSignBackground(@NotNull BlockState b, org.bukkit.Material material) { + if (b.getBlockData() instanceof WallSign) { + b.getBlock().getRelative(((WallSign) b.getBlockData()).getFacing().getOppositeFace()).setType(material); + } + } + + @Override + public void spigotShowPlayer(Player victim, @NotNull Player receiver) { + receiver.showPlayer(getPlugin(), victim); + } + + @Override + public void spigotHidePlayer(Player victim, @NotNull Player receiver) { + receiver.hidePlayer(getPlugin(), victim); + } + + + @Override + public void playRedStoneDot(@NotNull Player player) { + Color color = Color.RED; + PacketPlayOutWorldParticles particlePacket = new PacketPlayOutWorldParticles( + new ParticleParamRedstone( + new Vector3f((float) color.getRed(), + (float) color.getGreen(), + (float) color.getBlue()), (float) 1 + ), + true, + player.getLocation().getX(), + player.getLocation().getY() + 2.6, + player.getLocation().getZ(), + 0, 0, 0, 0, 0 + ); + for (Player inWorld : player.getWorld().getPlayers()) { + if (inWorld.equals(player)) continue; + this.sendPacket(inWorld, particlePacket); + } + } + + + @Override + public void clearArrowsFromPlayerBody(Player player) { + // minecraft clears them on death on newer version + } + + private @Nullable NBTTagCompound getTag(@NotNull ItemStack itemStack) { + // todo WIP + return new NBTTagCompound(); + } + + public NBTTagCompound getCreateTag(ItemStack itemStack) { + var tag = getTag(itemStack); + return null == tag ? initializeTag(itemStack) : tag; + } + + abstract @NotNull NBTTagCompound initializeTag(ItemStack itemStack); + + abstract EntityPlayer getPlayer(Player player); + + public List> getPlayerEquipment(@NotNull Player player) { + return getPlayerEquipment(getPlayer(player)); + } + + public List> getPlayerEquipment(@NotNull EntityPlayer entityPlayer) { + List> list = new ArrayList<>(); + list.add(new Pair<>(EnumItemSlot.a, entityPlayer.a(EnumItemSlot.a))); + list.add(new Pair<>(EnumItemSlot.b, entityPlayer.a(EnumItemSlot.b))); + list.add(new Pair<>(EnumItemSlot.f, entityPlayer.a(EnumItemSlot.f))); + list.add(new Pair<>(EnumItemSlot.e, entityPlayer.a(EnumItemSlot.e))); + list.add(new Pair<>(EnumItemSlot.d, entityPlayer.a(EnumItemSlot.d))); + list.add(new Pair<>(EnumItemSlot.c, entityPlayer.a(EnumItemSlot.c))); + + return list; + } + + @Override + public void placeTowerBlocks(@NotNull Block b, @NotNull IArena a, @NotNull TeamColor color, int x, int y, int z) { + b.getRelative(x, y, z).setType(color.woolMaterial()); + a.addPlacedBlock(b.getRelative(x, y, z)); + } + + @Override + public void placeLadder(@NotNull Block b, int x, int y, int z, @NotNull IArena a, int ladderData) { + Block block = b.getRelative(x, y, z); //ladder block + block.setType(Material.LADDER); + Ladder ladder = (Ladder) block.getBlockData(); + a.addPlacedBlock(block); + switch (ladderData) { + case 2 -> { + ladder.setFacing(BlockFace.NORTH); + block.setBlockData(ladder); + } + case 3 -> { + ladder.setFacing(BlockFace.SOUTH); + block.setBlockData(ladder); + } + case 4 -> { + ladder.setFacing(BlockFace.WEST); + block.setBlockData(ladder); + } + case 5 -> { + ladder.setFacing(BlockFace.EAST); + block.setBlockData(ladder); + } + } + } + + @Override + public void playVillagerEffect(@NotNull Player player, Location location) { + player.spawnParticle(Particle.HAPPY_VILLAGER, location, 1); + } + + abstract void sendPacket(Player player, Packet packet) ; + + abstract void sendPackets(Player player, Packet @NotNull ... packets); +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/AbstractDespawnableProvider.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/AbstractDespawnableProvider.java new file mode 100644 index 000000000..113216603 --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/AbstractDespawnableProvider.java @@ -0,0 +1,65 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +import com.andrei1058.bedwars.api.arena.team.ITeam; +import com.andrei1058.bedwars.api.server.VersionSupport; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityCreature; +import net.minecraft.world.entity.EntityInsentient; +import net.minecraft.world.entity.EntityLiving; +import net.minecraft.world.entity.ai.goal.PathfinderGoal; +import net.minecraft.world.entity.ai.goal.PathfinderGoalSelector; +import net.minecraft.world.entity.ai.goal.target.PathfinderGoalNearestAttackableTarget; +import net.minecraft.world.entity.player.EntityHuman; +import org.bukkit.attribute.Attribute; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public abstract class AbstractDespawnableProvider implements IDespawnableProvider { + + protected boolean notSameTeam(@NotNull Entity entity, ITeam team, @NotNull VersionSupport api) { + var despawnable = api.getDespawnablesList().getOrDefault(entity.getBukkitEntity().getUniqueId(), null); + return null == despawnable || despawnable.getTeam() != team; + } + + public PathfinderGoalSelector getTargetSelector(@NotNull EntityCreature entityLiving) { + return entityLiving.bW; + } + + public PathfinderGoalSelector getGoalSelector(@NotNull EntityCreature entityLiving) { + return entityLiving.bX; + } + + protected void clearSelectors(@NotNull EntityCreature entityLiving) { + getTargetSelector(entityLiving).b().clear(); + getGoalSelector(entityLiving).b().clear(); + } + + protected PathfinderGoal getTargetGoal(EntityInsentient entity, ITeam team, VersionSupport api) { + return new PathfinderGoalNearestAttackableTarget<>(entity, EntityLiving.class, 20, true, false, + entityLiving -> { + if (entityLiving instanceof EntityHuman) { + return !((EntityHuman) entityLiving).getBukkitEntity().isDead() && + !team.wasMember(((EntityHuman) entityLiving).getBukkitEntity().getUniqueId()) && + !team.getArena().isReSpawning(((EntityHuman) entityLiving).getBukkitEntity().getUniqueId()) + && !team.getArena().isSpectator(((EntityHuman) entityLiving).getBukkitEntity().getUniqueId()); + } + return notSameTeam(entityLiving, team, api); + }); + } + + protected void applyDefaultSettings(org.bukkit.entity.@NotNull LivingEntity bukkitEntity, DespawnableAttributes attr, + ITeam team) { + bukkitEntity.setRemoveWhenFarAway(false); + bukkitEntity.setPersistent(true); + bukkitEntity.setCustomNameVisible(true); + bukkitEntity.setCustomName(getDisplayName(attr, team)); + + // todo + var entity = ((EntityInsentient)((CraftEntity)bukkitEntity).getHandle()); + Objects.requireNonNull(entity.craftAttributes.getAttribute(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(attr.health()); + Objects.requireNonNull(entity.craftAttributes.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED)).setBaseValue(attr.speed()); + Objects.requireNonNull(entity.craftAttributes.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE)).setBaseValue(attr.damage()); + } +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableAttributes.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableAttributes.java new file mode 100644 index 000000000..d543a421a --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableAttributes.java @@ -0,0 +1,19 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +/** + * Customization of entity. + * + * @param type + * @param speed + * @param health + * @param damage + * @param removalTime entity un-spawn in seconds. + */ +public record DespawnableAttributes( + DespawnableType type, + double speed, + double health, + double damage, + int removalTime +) { +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableFactory.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableFactory.java new file mode 100644 index 000000000..24ce16394 --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableFactory.java @@ -0,0 +1,27 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +import com.andrei1058.bedwars.api.arena.team.ITeam; +import com.andrei1058.bedwars.api.server.VersionSupport; +import org.bukkit.Location; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class DespawnableFactory { + + private final VersionSupport versionSupport; + private final List> providers = new ArrayList<>(); + + public DespawnableFactory(VersionSupport versionSupport) { + this.versionSupport = versionSupport; + providers.add(new TeamIronGolem()); + providers.add(new TeamSilverfish()); + } + + public LivingEntity spawn(@NotNull DespawnableAttributes attr, @NotNull Location location, @NotNull ITeam team){ + return providers.stream().filter(provider -> provider.getType() == attr.type()) + .findFirst().orElseThrow().spawn(attr, location,team, versionSupport); + } +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableType.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableType.java new file mode 100644 index 000000000..f699483aa --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/DespawnableType.java @@ -0,0 +1,6 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +public enum DespawnableType { + IRON_GOLEM, + SILVERFISH +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/IDespawnableProvider.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/IDespawnableProvider.java new file mode 100644 index 000000000..cb1235ba7 --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/IDespawnableProvider.java @@ -0,0 +1,21 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +import com.andrei1058.bedwars.api.arena.team.ITeam; +import com.andrei1058.bedwars.api.server.VersionSupport; +import net.minecraft.world.entity.EntityCreature; +import net.minecraft.world.entity.ai.goal.PathfinderGoalSelector; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; + +public interface IDespawnableProvider { + + DespawnableType getType(); + + String getDisplayName(DespawnableAttributes attr, ITeam team); + + T spawn(@NotNull DespawnableAttributes attr, @NotNull Location location, @NotNull ITeam team, VersionSupport api); + + PathfinderGoalSelector getTargetSelector(@NotNull EntityCreature entityLiving); + + PathfinderGoalSelector getGoalSelector(@NotNull EntityCreature entityLiving); +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/TeamIronGolem.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/TeamIronGolem.java new file mode 100644 index 000000000..e4bf388f6 --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/TeamIronGolem.java @@ -0,0 +1,59 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +import com.andrei1058.bedwars.api.arena.team.ITeam; +import com.andrei1058.bedwars.api.language.Language; +import com.andrei1058.bedwars.api.language.Messages; +import com.andrei1058.bedwars.api.server.VersionSupport; +import net.minecraft.world.entity.ai.goal.PathfinderGoalFloat; +import net.minecraft.world.entity.ai.goal.PathfinderGoalMeleeAttack; +import net.minecraft.world.entity.ai.goal.PathfinderGoalRandomLookaround; +import net.minecraft.world.entity.ai.goal.PathfinderGoalRandomStroll; +import net.minecraft.world.entity.ai.goal.target.PathfinderGoalHurtByTarget; +import net.minecraft.world.entity.animal.EntityIronGolem; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftEntity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.IronGolem; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class TeamIronGolem extends AbstractDespawnableProvider { + + @Override + public DespawnableType getType() { + return DespawnableType.IRON_GOLEM; + } + + @Override + public String getDisplayName(@NotNull DespawnableAttributes attr, @NotNull ITeam team) { + Language lang = Language.getDefaultLanguage(); + return lang.m(Messages.SHOP_UTILITY_NPC_IRON_GOLEM_NAME).replace("{despawn}", String.valueOf(attr.removalTime()) + .replace("{health}", StringUtils.repeat(lang.m(Messages.FORMATTING_DESPAWNABLE_UTILITY_NPC_HEALTH) + " ", 10)) + .replace("{TeamColor}", team.getColor().chat().toString()) + ); + } + + public @NotNull IronGolem spawn(@NotNull DespawnableAttributes attr, @NotNull Location location, @NotNull ITeam team, VersionSupport api) { + + var bukkitEntity = (IronGolem) Objects.requireNonNull(location.getWorld()).spawnEntity(location, EntityType.IRON_GOLEM); + applyDefaultSettings(bukkitEntity, attr, team); + + // todo + var entity = (EntityIronGolem) ((CraftEntity) bukkitEntity).getHandle(); + + clearSelectors(entity); + var goalSelector = getGoalSelector(entity); + var targetSelector = getTargetSelector(entity); + + goalSelector.a(1, new PathfinderGoalFloat(entity)); + goalSelector.a(2, new PathfinderGoalMeleeAttack(entity, 1.5D, false)); + goalSelector.a(3, new PathfinderGoalRandomStroll(entity, 1D)); + goalSelector.a(4, new PathfinderGoalRandomLookaround(entity)); + targetSelector.a(1, new PathfinderGoalHurtByTarget(entity)); + targetSelector.a(2, getTargetGoal(entity, team, api)); + + return bukkitEntity; + } +} diff --git a/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/TeamSilverfish.java b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/TeamSilverfish.java new file mode 100644 index 000000000..d94e17318 --- /dev/null +++ b/versionsupport_nms_cmn1/src/main/java/dev/andrei1058/mc/bedwars/despawnable/TeamSilverfish.java @@ -0,0 +1,57 @@ +package dev.andrei1058.mc.bedwars.despawnable; + +import com.andrei1058.bedwars.api.arena.team.ITeam; +import com.andrei1058.bedwars.api.language.Language; +import com.andrei1058.bedwars.api.language.Messages; +import com.andrei1058.bedwars.api.server.VersionSupport; +import net.minecraft.world.entity.ai.goal.PathfinderGoalFloat; +import net.minecraft.world.entity.ai.goal.PathfinderGoalMeleeAttack; +import net.minecraft.world.entity.ai.goal.PathfinderGoalRandomLookaround; +import net.minecraft.world.entity.ai.goal.PathfinderGoalRandomStroll; +import net.minecraft.world.entity.ai.goal.target.PathfinderGoalHurtByTarget; +import net.minecraft.world.entity.monster.EntitySilverfish; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftEntity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Silverfish; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class TeamSilverfish extends AbstractDespawnableProvider { + @Override + public DespawnableType getType() { + return DespawnableType.SILVERFISH; + } + + @Override + public String getDisplayName(@NotNull DespawnableAttributes attr, @NotNull ITeam team) { + Language lang = Language.getDefaultLanguage(); + return lang.m(Messages.SHOP_UTILITY_NPC_SILVERFISH_NAME).replace("{despawn}", String.valueOf(attr.removalTime()) + .replace("{health}", StringUtils.repeat(lang.m(Messages.FORMATTING_DESPAWNABLE_UTILITY_NPC_HEALTH) + " ", 10)) + .replace("{TeamColor}", team.getColor().chat().toString()) + ); + } + + @Override + public Silverfish spawn(@NotNull DespawnableAttributes attr, @NotNull Location location, @NotNull ITeam team, VersionSupport api) { + var bukkitEntity = (Silverfish) Objects.requireNonNull(location.getWorld()).spawnEntity(location, EntityType.SILVERFISH); + applyDefaultSettings(bukkitEntity, attr, team); + + // todo + var entity = (EntitySilverfish) ((CraftEntity) bukkitEntity).getHandle(); + clearSelectors(entity); + + var goalSelector = getGoalSelector(entity); + var targetSelector = getTargetSelector(entity); + goalSelector.a(1, new PathfinderGoalFloat(entity)); + goalSelector.a(2, new PathfinderGoalMeleeAttack(entity, 1.9D, false)); + goalSelector.a(3, new PathfinderGoalRandomStroll(entity, 2D)); + goalSelector.a(4, new PathfinderGoalRandomLookaround(entity)); + targetSelector.a(1, new PathfinderGoalHurtByTarget(entity)); + targetSelector.a(2, getTargetGoal(entity, team, api)); + + return bukkitEntity; + } +} diff --git a/versionsupport_v1_21_R1/pom.xml b/versionsupport_v1_21_R1/pom.xml new file mode 100644 index 000000000..6dfef10ee --- /dev/null +++ b/versionsupport_v1_21_R1/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.andrei1058.bedwars + BedWars1058 + 23.12.1-SNAPSHOT + + + versionsupport_v1_21_R1 + + + 20 + 20 + UTF-8 + + + + + com.andrei1058.bedwars + versionsupport_nms_cmn1 + ${project.version} + provided + + + com.andrei1058.bedwars + bedwars-api + ${project.version} + jar + provided + + + com.andrei1058.bedwars + versionsupport-common + ${project.version} + jar + provided + + + org.spigotmc + spigot + 1.21.1-R0.1-SNAPSHOT + jar + provided + + + commons-codec + commons-codec + + + + + + + + + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 20 + 20 + + + + + + \ No newline at end of file diff --git a/versionsupport_v1_21_R1/src/main/java/com/andrei1058/bedwars/support/version/v1_21_R1/v1_21_R1.java b/versionsupport_v1_21_R1/src/main/java/com/andrei1058/bedwars/support/version/v1_21_R1/v1_21_R1.java new file mode 100644 index 000000000..28193900b --- /dev/null +++ b/versionsupport_v1_21_R1/src/main/java/com/andrei1058/bedwars/support/version/v1_21_R1/v1_21_R1.java @@ -0,0 +1,244 @@ +package com.andrei1058.bedwars.support.version.v1_21_R1; + +import com.mojang.datafixers.util.Pair; +import dev.andrei1058.mc.bedwars.AbstractVerImplCmn1; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.server.network.PlayerConnection; +import net.minecraft.world.entity.EntityLiving; +import net.minecraft.world.entity.EnumItemSlot; +import net.minecraft.world.entity.item.EntityTNTPrimed; +import net.minecraft.world.entity.projectile.EntityFireball; +import net.minecraft.world.entity.projectile.IProjectile; +import net.minecraft.world.item.*; +import org.bukkit.command.Command; +import org.bukkit.craftbukkit.v1_21_R1.CraftServer; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftFireball; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_21_R1.entity.CraftTNTPrimed; +import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import static org.bukkit.craftbukkit.v1_21_R1.util.CraftMagicNumbers.getItem; + +@SuppressWarnings("unused") +public class v1_21_R1 extends AbstractVerImplCmn1 { + + public v1_21_R1(Plugin plugin, String name) { + super(plugin, name); + } + + @Override + public void registerCommand(String name, Command cmd) { + ((CraftServer) getPlugin().getServer()).getCommandMap().register(name, cmd); + } + + public @Nullable NBTTagCompound getTag(@NotNull org.bukkit.inventory.ItemStack itemStack) { + var i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + return null; + } + // TODO WIP + return new NBTTagCompound(); + } + + @Override + public boolean isBukkitCommandRegistered(String name) { + return ((CraftServer) getPlugin().getServer()).getCommandMap().getCommand(name) != null; + } + + @Override + public void setSource(TNTPrimed tnt, Player owner) { + EntityLiving nmsEntityLiving = (((CraftLivingEntity) owner).getHandle()); + EntityTNTPrimed nmsTNT = (((CraftTNTPrimed) tnt).getHandle()); + try { + Field sourceField = EntityTNTPrimed.class.getDeclaredField("d"); + sourceField.setAccessible(true); + sourceField.set(nmsTNT, nmsEntityLiving); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public boolean isArmor(org.bukkit.inventory.ItemStack itemStack) { + var i = getItem(itemStack); + if (null == i) return false; + return i instanceof ItemArmor || i instanceof ItemElytra; + } + + @Override + public boolean isTool(org.bukkit.inventory.ItemStack itemStack) { + var i = getItem(itemStack); + if (null == i) return false; + return i instanceof ItemTool; + } + + @Override + public boolean isSword(org.bukkit.inventory.ItemStack itemStack) { + var i = getItem(itemStack); + if (null == i) return false; + return i instanceof ItemSword; + } + + @Override + public boolean isAxe(org.bukkit.inventory.ItemStack itemStack) { + var i = getItem(itemStack); + if (null == i) return false; + return i instanceof ItemAxe; + } + + @Override + public boolean isBow(org.bukkit.inventory.ItemStack itemStack) { + var i = getItem(itemStack); + if (null == i) return false; + return i instanceof ItemBow; + } + + @Override + public boolean isProjectile(org.bukkit.inventory.ItemStack itemStack) { + var entity = getEntity(itemStack); + if (null == entity) return false; + return entity instanceof IProjectile; + } + + @Override + public void voidKill(Player p) { + EntityPlayer player = getPlayer(p); + player.a(player.dM().m(), 1000); + } + @Override + public void showArmor(@NotNull Player victim, Player receiver) { + List> items = new ArrayList<>(); + items.add(new Pair<>(EnumItemSlot.f, CraftItemStack.asNMSCopy(victim.getInventory().getHelmet()))); + items.add(new Pair<>(EnumItemSlot.e, CraftItemStack.asNMSCopy(victim.getInventory().getChestplate()))); + items.add(new Pair<>(EnumItemSlot.d, CraftItemStack.asNMSCopy(victim.getInventory().getLeggings()))); + items.add(new Pair<>(EnumItemSlot.c, CraftItemStack.asNMSCopy(victim.getInventory().getBoots()))); + PacketPlayOutEntityEquipment packet1 = new PacketPlayOutEntityEquipment(victim.getEntityId(), items); + sendPacket(receiver, packet1); + } + + @Override + public String getMainLevel() { + //noinspection deprecation + return ((DedicatedServer) MinecraftServer.getServer()).a().n; + } + + + @Override + public Fireball setFireballDirection(Fireball fireball, @NotNull Vector vector) { + EntityFireball fb = ((CraftFireball) fireball).getHandle(); + fb.b = vector.getX() * 0.1D; + fb.c = vector.getY() * 0.1D; + fb.d = vector.getZ() * 0.1D; + return (Fireball) fb.getBukkitEntity(); + } + + + /** + * Gets the NMS Item from ItemStack + */ + private @Nullable Item getItem(org.bukkit.inventory.ItemStack itemStack) { + var i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + return null; + } + return i.g(); + } + + + /** + * Gets the NMS Entity from ItemStack + */ + private @Nullable net.minecraft.world.entity.Entity getEntity(org.bukkit.inventory.ItemStack itemStack) { + var i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + return null; + } + return i.H(); + } + + @Nullable + public NBTTagCompound getTag(@NotNull org.bukkit.inventory.ItemStack itemStack) { + var i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + return null; + } + return i.v(); + } + + private @NotNull NBTTagCompound initializeTag(org.bukkit.inventory.ItemStack itemStack) { + var i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + throw new RuntimeException("Cannot convert given item to a NMS item"); + } + return initializeTag(i); + } + + private @NotNull NBTTagCompound initializeTag(ItemStack itemStack) { + + var tag = getTag(itemStack); + if (null != tag) { + throw new RuntimeException("Provided item already has a Tag"); + } + tag = new NBTTagCompound(); + itemStack.c(tag); + + return tag; + } + + public NBTTagCompound getCreateTag(org.bukkit.inventory.ItemStack itemStack) { + var i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + throw new RuntimeException("Cannot convert given item to a NMS item"); + } + return getCreateTag(i); + } + + public org.bukkit.inventory.ItemStack applyTag(org.bukkit.inventory.ItemStack itemStack, NBTTagCompound tag) { + return CraftItemStack.asBukkitCopy(applyTag(getNmsItemCopy(itemStack), tag)); + } + + public ItemStack applyTag(@NotNull ItemStack itemStack, NBTTagCompound tag) { + itemStack.(tag); + return itemStack; + } + + public ItemStack getNmsItemCopy(org.bukkit.inventory.ItemStack itemStack) { + ItemStack i = CraftItemStack.asNMSCopy(itemStack); + if (null == i) { + throw new RuntimeException("Cannot convert given item to a NMS item"); + } + return i; + } + + public EntityPlayer getPlayer(Player player) { + return ((CraftPlayer) player).getHandle(); + } + + public void sendPacket(Player player, Packet packet) { + ((CraftPlayer) player).getHandle().c.a(packet); + } + + public void sendPackets(Player player, Packet @NotNull ... packets) { + PlayerConnection connection = ((CraftPlayer) player).getHandle().c; + for (Packet p : packets) { + connection.a(p); + } + } +} \ No newline at end of file