Use PersistentDataContainer for flying bows

This commit is contained in:
NotAlexNoyle committed 2026-05-25 17:55:17 -04:00
1 parent cc1c71dad9
commit 3bedc7448c
4 files changed
+47 -43

No files matched your search

@@ -6,12 +6,12 @@ import java.util.Collections;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.trueog.flyingbowog.FlyingBowOG;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@@ -38,9 +38,10 @@ public class GiveCommand implements CommandExecutor {
ItemMeta bowMeta = flyingBowItem.getItemMeta();
bowMeta.displayName(Component.text("Flying Bow").color(TextColor.color(0, 255, 255))
.decorate(TextDecoration.ITALIC));
bowMeta.lore(Collections.singletonList(Component.text("Shoot this bow to FLY")));
bowMeta.lore(Collections.singletonList(Component.text(FlyingBowOG.LORE_MARKER)));
// Tag as a Flying Bow via PersistentDataContainer (Panilla-legal NBT).
FlyingBowOG.tagFlyingBow(bowMeta);
flyingBowItem.setItemMeta(bowMeta);
flyingBowItem.addItemFlags(ItemFlag.HIDE_DYE);
player.getInventory().addItem(flyingBowItem);
@@ -17,37 +17,43 @@ import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin;
public final class FlyingBowOG extends JavaPlugin implements Listener {
private static FlyingBowOG plugin;
// PersistentDataContainer key identifying a Flying Bow. Stored under the
// vanilla-legal PublicBukkitValues NBT compound so anti-illegal-item plugins
// (e.g. Panilla) do not strip it, unlike the old HideFlags-based marker.
private static NamespacedKey flyingBowKey;
// Lore line shown on every Flying Bow (display only; identity is the PDC tag).
public static final String LORE_MARKER = "Shoot this bow to FLY";
@Override
public void onEnable() {
// Plugin startup logic
plugin = this;
flyingBowKey = new NamespacedKey(this, "flying_bow");
// create the flying bow item
ItemStack flyingBowItem = new ItemStack(Material.BOW);
ItemMeta bowMeta = flyingBowItem.getItemMeta();
bowMeta.displayName(
Component.text("Flying Bow").color(TextColor.color(0, 255, 255)).decorate(TextDecoration.ITALIC));
bowMeta.lore(Collections.singletonList(Component.text("Shoot this bow to FLY")));
bowMeta.lore(Collections.singletonList(Component.text(LORE_MARKER)));
// Tag as a Flying Bow via PersistentDataContainer (Panilla-legal NBT).
tagFlyingBow(bowMeta);
flyingBowItem.setItemMeta(bowMeta);
// adding a useless itemFlag to identify this as a flying bow
// this was easier for me than adding an attribute, and no other bow should have
// this flag
flyingBowItem.addItemFlags(ItemFlag.HIDE_DYE);
// add the recipe into the game
ShapedRecipe flyingBowRecipe = new ShapedRecipe(new NamespacedKey(this, "flying_bow"), flyingBowItem);
ShapedRecipe flyingBowRecipe = new ShapedRecipe(flyingBowKey, flyingBowItem);
flyingBowRecipe.shape("sd ", "sed", "sd ");
flyingBowRecipe.setIngredient('s', Material.STICK);
flyingBowRecipe.setIngredient('e', Material.ELYTRA);
@@ -72,6 +78,28 @@ public final class FlyingBowOG extends JavaPlugin implements Listener {
}
// Stamps a Flying Bow marker into an item's PersistentDataContainer.
public static void tagFlyingBow(ItemMeta meta) {
meta.getPersistentDataContainer().set(flyingBowKey, PersistentDataType.BYTE, (byte) 1);
}
// Canonical Flying Bow identity check used by every listener. Identity lives in
// the PDC marker only (Panilla-legal NBT).
public static boolean isFlyingBow(ItemStack item) {
if (item == null || item.getType() != Material.BOW)
return false;
ItemMeta meta = item.getItemMeta();
if (meta == null)
return false;
return meta.getPersistentDataContainer().has(flyingBowKey, PersistentDataType.BYTE);
}
public static String getPrefix() {
return "&7[&5FlyingBow&f-&4OG&7] ";
@@ -16,7 +16,6 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionType;
@@ -32,19 +31,10 @@ public class OnEntityShootBowEvent implements Listener {
// Only react to players shooting arrows with our Flying Bow.
if (!(event.getEntity() instanceof Player player))
return;
FlyingBowOG.getPlugin().getLogger().info("[FB-DEBUG] shoot by player " + player.getName());
if (!(event.getProjectile() instanceof Arrow arrow)) {
FlyingBowOG.getPlugin().getLogger()
.info("[FB-DEBUG] projectile NOT Arrow: " + event.getProjectile().getType());
if (!(event.getProjectile() instanceof Arrow arrow))
return;
}
ItemStack bow = player.getInventory().getItemInMainHand();
FlyingBowOG.getPlugin().getLogger().info("[FB-DEBUG] mainhand=" + bow.getType() + " flags=" + bow.getItemFlags()
+ " isFlyingBow=" + isFlyingBow(bow));
if (!isFlyingBow(bow))
if (!FlyingBowOG.isFlyingBow(bow))
return;
// Block flying bow usage while player is already wearing an elytra.
@@ -80,15 +70,10 @@ public class OnEntityShootBowEvent implements Listener {
boolean sameWorld = shooter.getWorld().getKey().toString().equals(worldKey);
FlyingBowOG.getPlugin().getLogger().info("[FB-DEBUG] mount tick: sameWorld=" + sameWorld + " dead="
+ rideArrow.isDead() + " valid=" + rideArrow.isValid());
if (!sameWorld || rideArrow.isDead() || !rideArrow.isValid())
return;
boolean mounted = rideArrow.addPassenger(shooter);
FlyingBowOG.getPlugin().getLogger()
.info("[FB-DEBUG] addPassenger returned " + mounted + " passengers=" + rideArrow.getPassengers());
rideArrow.addPassenger(shooter);
}, 1L);
@@ -142,7 +127,7 @@ public class OnEntityShootBowEvent implements Listener {
Player player = event.getPlayer();
boolean holdingFlyingBow = isFlyingBow(player.getInventory().getItemInMainHand());
boolean holdingFlyingBow = FlyingBowOG.isFlyingBow(player.getInventory().getItemInMainHand());
boolean ridingFlyingBowArrow = isRidingTaggedArrow(player);
if (!(holdingFlyingBow || ridingFlyingBowArrow))
@@ -165,13 +150,6 @@ public class OnEntityShootBowEvent implements Listener {
}
// Identifies your custom “Flying Bow”.
private boolean isFlyingBow(ItemStack item) {
return item != null && item.getType() == Material.BOW && item.getItemFlags().contains(ItemFlag.HIDE_DYE);
}
// True if the player is riding one of the cyan, UNCRAFTABLE arrows.
private boolean isRidingTaggedArrow(Player player) {
@@ -11,7 +11,6 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
@@ -31,7 +30,7 @@ public class OnPrepareAnvilEvent implements Listener {
if (secondItem != null) {
if (firstItem.getItemFlags().contains(ItemFlag.HIDE_DYE) && firstItem.getType().equals(Material.BOW)) {
if (FlyingBowOG.isFlyingBow(firstItem)) {
if (secondItem.getType().equals(Material.PHANTOM_MEMBRANE)) {
@@ -104,10 +103,8 @@ public class OnPrepareAnvilEvent implements Listener {
// if the second item is a bow
if (secondItem.getType().equals(Material.BOW)) {
// if only one bow has the HIDE_DYE flag
if (firstItem.getItemFlags().contains(ItemFlag.HIDE_DYE)
^ secondItem.getItemFlags().contains(ItemFlag.HIDE_DYE))
{
// if only one of the two bows is a flying bow
if (FlyingBowOG.isFlyingBow(firstItem) ^ FlyingBowOG.isFlyingBow(secondItem)) {
// set the result to null
event.setResult(null);