From 96e4a8e380fc9fa60d1981ac4513efb45abd5db0 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD Date: Tue, 24 Jun 2025 18:38:11 -0500 Subject: [PATCH] Refactor for Events --- .../java/me/athlaeos/piles/PileRegistry.java | 225 ++++++++----- .../java/me/athlaeos/piles/domain/Pile.java | 23 +- .../java/me/athlaeos/piles/domain/Pos.java | 24 ++ .../piles/listeners/PilesListener.java | 313 +++++++++--------- 4 files changed, 352 insertions(+), 233 deletions(-) diff --git a/src/main/java/me/athlaeos/piles/PileRegistry.java b/src/main/java/me/athlaeos/piles/PileRegistry.java index e26971b..f479c3e 100644 --- a/src/main/java/me/athlaeos/piles/PileRegistry.java +++ b/src/main/java/me/athlaeos/piles/PileRegistry.java @@ -7,7 +7,6 @@ import com.google.gson.JsonSyntaxException; import me.athlaeos.piles.adapters.GsonAdapter; import me.athlaeos.piles.adapters.ItemStackGSONAdapter; -import me.athlaeos.piles.config.ConfigManager; import me.athlaeos.piles.domain.Pile; import me.athlaeos.piles.domain.PileQuantityCounter; import me.athlaeos.piles.piles.ComplexPile; @@ -18,14 +17,21 @@ import me.athlaeos.piles.utils.Utils; import org.bukkit.*; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; @@ -45,7 +51,7 @@ public class PileRegistry { .disableHtmlEscaping() .enableComplexMapKeySerialization() .create(); - private static final Map registeredPiles = new HashMap<>(); + private static final Map registeredPiles = new LinkedHashMap<>(); private static final Map registeredSelectors = new HashMap<>(); private static final Map activePilesByPosition = new HashMap<>(); private static PileQuantityCounter quantityCounter = null; @@ -71,144 +77,179 @@ public class PileRegistry { }); } - public static void registerSelector(PileTypeSelector selector){ - registeredSelectors.put(selector.getIdentifier(), selector); - } - - public static Map getRegisteredSelectors() { - return new HashMap<>(registeredSelectors); - } - - public static PileType typeFromItem(ItemStack item){ - List pilesByPriority = new ArrayList<>(registeredPiles.values()); - pilesByPriority.sort(Comparator.comparingInt(PileType::priority)); - for (PileType pile : pilesByPriority){ - if (pile.acceptsItem(item)) return pile; - } - return null; - } - public static Pile fromBlock(Block b){ - Pos pos = new Pos(b.getWorld().getName(), b.getX(), b.getY(), b.getZ()); + Pos pos = new Pos(b); Pile pile = activePilesByPosition.get(pos); - if (pile == null){ - for (Entity e : b.getWorld().getNearbyEntities(b.getLocation(), 1, 1, 1)){ - if (!(e instanceof ItemDisplay i) || !i.getPersistentDataContainer().has(PILE_TYPE, PersistentDataType.STRING)) continue; - Pile p = fromEntity(i); - if (p == null || !p.getPosition().getWorld().equalsIgnoreCase(b.getWorld().getName()) || - p.getPosition().getX() != b.getX() || p.getPosition().getY() != b.getY() || p.getPosition().getZ() != b.getZ()) continue; - pile = p; + if (pile != null) { + return pile; + } + + for (Entity e : b.getWorld().getNearbyEntities(b.getLocation(), 1, 1, 1, PileRegistry::isPile)){ + Pile p = fromEntity((ItemDisplay) e); + if (p != null && p.getPosition().equals(pos)) { activePilesByPosition.put(pos, p); + return p; } } - return pile; + return null; } public static Pile fromEntity(ItemDisplay display){ - if (!display.getPersistentDataContainer().has(PILE_TYPE, PersistentDataType.STRING)) return null; - PileType pileType = getPileType(display.getPersistentDataContainer().get(PILE_TYPE, PersistentDataType.STRING)); - String encodedItems = display.getPersistentDataContainer().getOrDefault(PILE_ITEMS, PersistentDataType.STRING, ""); + if (!isPile(display)) return null; + PersistentDataContainer pdc = display.getPersistentDataContainer(); + PileType pileType = getPileType(pdc.get(PILE_TYPE, PersistentDataType.STRING)); + + String encodedItems = pdc.getOrDefault(PILE_ITEMS, PersistentDataType.STRING, ""); String[] itemEntries = encodedItems.split(""); List items = new ArrayList<>(); - if (!encodedItems.isEmpty()) for (String entry : itemEntries) items.add(Utils.deserialize(entry)); + if (!encodedItems.isEmpty()) { + for (String entry : itemEntries) { + items.add(Utils.deserialize(entry)); + } + } + if (pileType == null) { // destroy if pile type was deleted - items.forEach(i -> display.getWorld().dropItemNaturally(display.getLocation().subtract(0.5, 0.5, 0.5), i)); + for (ItemStack i : items) { + display.getWorld().dropItemNaturally(display.getLocation().subtract(0.5, 0.5, 0.5), i); + } display.remove(); if (display.getLocation().getBlock().getType() == Material.BARRIER) display.getLocation().getBlock().setType(Material.AIR); return null; } - String uuid = display.getPersistentDataContainer().get(PILE_OWNER, PersistentDataType.STRING); + + String uuid = pdc.get(PILE_OWNER, PersistentDataType.STRING); UUID owner = uuid == null ? null : UUID.fromString(uuid); - String encodedPos = display.getPersistentDataContainer().getOrDefault(PILE_POSITION, PersistentDataType.STRING, ""); + + String encodedPos = pdc.getOrDefault(PILE_POSITION, PersistentDataType.STRING, ""); String[] posEntries = encodedPos.split(","); String world = posEntries[0]; int x = Integer.parseInt(posEntries[1]); int y = Integer.parseInt(posEntries[2]); int z = Integer.parseInt(posEntries[3]); - Pos realPos = new Pos(world, x, y, z); - return new Pile(pileType, realPos, owner, display, items); + Pos pos = new Pos(world, x, y, z); + + return new Pile(pileType, pos, owner, display, items); + } + + public static boolean isPile(Block b) { + return fromBlock(b) != null; + } + + public static boolean isPile(Entity entity){ + return entity instanceof ItemDisplay display && isPile(display); } public static boolean isPile(ItemDisplay display){ return display.getPersistentDataContainer().has(PILE_TYPE, PersistentDataType.STRING); } - public static boolean placePile(Player by, ItemStack item, Block b, float rotation){ + public static boolean placePile(@Nonnull Player by, ItemStack item, Block b, float rotation){ PileType type = typeFromItem(item); if (type == null) return false; Pile existingPile = fromBlock(b); - return placePile(by, item, type, existingPile, b.getLocation(), rotation); + return placePile(by, item, type, existingPile, b, rotation); } - public static boolean placePile(Player by, ItemStack item, ItemDisplay d, float rotation){ + public static boolean placePile(@Nonnull Player by, ItemStack item, ItemDisplay d, float rotation){ PileType type = typeFromItem(item); if (type == null) return false; Pile existingPile = fromEntity(d); - return placePile(by, item, type, existingPile, d.getLocation(), rotation); + return placePile(by, item, type, existingPile, d.getLocation().getBlock(), rotation); } - private static boolean canPlace(Chunk chunk){ - int limit = Piles.getPluginConfig().getInt("chunk_pile_limit"); - int found = 0; - for (Entity entity : chunk.getEntities()){ - if (entity instanceof ItemDisplay i && isPile(i)) found++; + private static boolean canPlace(@Nonnull Player player, ItemStack item, Block block, boolean newPile) { + if (!player.hasPermission("piles.place") || (newPile && !canPlacePiles(player)) || hasPlacementBlocked(player)) { + return false; + } + + if (newPile) { + int found = 0; + for (Entity entity : block.getChunk().getEntities()){ + if (isPile(entity)) found++; + } + + int limit = Piles.getPluginConfig().getInt("chunk_pile_limit"); + if (found >= limit) { + Utils.sendMessage(player, Piles.getPluginConfig().getString("message_pile_chunk_limit_reached", "") + .replace("%amount%", String.valueOf(limit))); + return false; + } } - return found < limit; + + BlockPlaceEvent event = new BlockPlaceEvent(block, block.getState(), block.getRelative(BlockFace.DOWN), item, player, true, EquipmentSlot.HAND); + Bukkit.getPluginManager().callEvent(event); + return !event.isCancelled(); } - private static boolean placePile(Player by, ItemStack item, PileType type, Pile pile, Location l, float rotation){ - if (pile == null && (by == null || !by.isOp()) && !canPlace(l.getChunk())) { - Utils.sendMessage(by, Piles.getPluginConfig().getString("message_pile_chunk_limit_reached", "").replace("%amount%", String.valueOf(Piles.getPluginConfig().getInt("chunk_pile_limit")))); + private static boolean placePile(@Nonnull Player player, ItemStack item, PileType type, Pile pile, Block block, float rotation) { + if (!type.acceptsItem(item) || !type.canPlace(block) + || (pile != null && (!pile.isValid() || !pile.getPile().equals(type.getType()) || pile.getItems().size() >= type.getMaxSize())) + || !canPlace(player, item, block, pile == null)) { return false; } - if (l.getWorld() == null || (by != null && (!by.hasPermission("piles.place") || !canPlacePiles(by, pile == null) || hasPlacementBlocked(by))) || !type.acceptsItem(item) || !type.canPlace(l.getBlock())) return false; + + Pos pos = new Pos(block); item = item.clone(); item.setAmount(1); - Pos pos = new Pos(l.getWorld().getName(), l.getBlockX(), l.getBlockY(), l.getBlockZ()); + ItemDisplay display; if (pile == null) { - display = l.getWorld().spawn(l.add(0.5, 0.5, 0.5), ItemDisplay.class); - display.setRotation(rotation, 0); - display.getPersistentDataContainer().set(PILE_TYPE, PersistentDataType.STRING, type.getType()); - if (by != null) display.getPersistentDataContainer().set(PILE_OWNER, PersistentDataType.STRING, by.getUniqueId().toString()); - display.getPersistentDataContainer().set(PILE_POSITION, PersistentDataType.STRING, String.format("%s,%d,%d,%d", pos.getWorld(), pos.getX(), pos.getY(), pos.getZ())); - pile = new Pile(type, pos, by == null ? null : by.getUniqueId(), display, new ArrayList<>()); - if (type.isSolid()) l.getWorld().getBlockAt(l).setType(Material.BARRIER); + display = block.getWorld().spawn(block.getLocation().add(0.5, 0.5, 0.5), ItemDisplay.class, spawned -> { + PersistentDataContainer pdc = spawned.getPersistentDataContainer(); + pdc.set(PILE_TYPE, PersistentDataType.STRING, type.getType()); + pdc.set(PILE_OWNER, PersistentDataType.STRING, player.getUniqueId().toString()); + pdc.set(PILE_POSITION, PersistentDataType.STRING, pos.toString()); + spawned.setRotation(rotation, 0); + }); + pile = new Pile(type, pos, player.getUniqueId(), display, new ArrayList<>()); + if (type.isSolid()) block.setType(Material.BARRIER); activePilesByPosition.put(pos, pile); - if (by != null && !by.isOp()) quantityCounter.getPileQuantities().put(by.getUniqueId(), quantityCounter.getPileQuantities().getOrDefault(by.getUniqueId(), 0) + 1); + if (!player.isOp()) quantityCounter.getPileQuantities().put(player.getUniqueId(), quantityCounter.getPileQuantities().getOrDefault(player.getUniqueId(), 0) + 1); } else { - if (pile.getItems().size() >= type.getMaxSize()) return false; // pile is full display = pile.getDisplay(); - if (!display.getPersistentDataContainer().getOrDefault(PILE_TYPE, PersistentDataType.STRING, type.getType()).equals(type.getType())) return false; } - if (type.getPlacementSound() != null) l.getWorld().playSound(l, type.getPlacementSound(), 1F, 1F); + + if (type.getPlacementSound() != null) { + block.getWorld().playSound(block.getLocation(), type.getPlacementSound(), 1F, 1F); + } pile.addItem(item); - display.getPersistentDataContainer().set(PILE_ITEMS, PersistentDataType.STRING, pile.getItems().stream().map(Utils::serialize).collect(Collectors.joining(""))); + display.getPersistentDataContainer().set(PILE_ITEMS, PersistentDataType.STRING, pile.serializeItems()); display.setItemStack(type.getFinalDisplay(pile.getItems().size())); return true; } - public static void destroyPile(Player destroyer, Block b){ + public static boolean destroyPile(@Nullable Player destroyer, Block b){ Pile existingPile = fromBlock(b); - if (existingPile == null) return; - destroyPile(existingPile, destroyer); + if (existingPile == null) return false; + return destroyPile(existingPile, destroyer); } - public static void destroyPile(Player destroyer, ItemDisplay d){ + public static boolean destroyPile(@Nullable Player destroyer, ItemDisplay d){ Pile existingPile = fromEntity(d); - if (existingPile == null) return; - destroyPile(existingPile, destroyer); + if (existingPile == null) return false; + return destroyPile(existingPile, destroyer); } - public static boolean canDestroy(Player p, Pile pile){ - boolean pileProtection = Piles.getPluginConfig().getBoolean("pile_protection", false); - if (!pileProtection) return true; - return p != null && (p.isOp() || pile.getOwner().equals(p.getUniqueId())); + public static boolean canDestroy(@Nullable Player p, Pile pile){ + Block block = pile.getPosition().getBlock(); + if (block == null) { + return false; + } else if (Piles.getPluginConfig().getBoolean("pile_protection", false) + && (p != null && !p.isOp() && !pile.getOwner().equals(p.getUniqueId()))) { + return false; + } else if (p == null) { + return true; + } + + BlockBreakEvent event = new BlockBreakEvent(block, p); + event.setDropItems(false); + Bukkit.getPluginManager().callEvent(event); + return !event.isCancelled(); } - private static void destroyPile(Pile pile, Player destroyer){ - if (!canDestroy(destroyer, pile)) return; + private static boolean destroyPile(Pile pile, @Nullable Player destroyer){ + if (!canDestroy(destroyer, pile)) return false; ItemDisplay display = pile.getDisplay(); PileType type = getPileType(pile.getPile()); quantityCounter.getPileQuantities().put(pile.getOwner(), Math.max(0, quantityCounter.getPileQuantities().getOrDefault(pile.getOwner(), 0) - 1)); @@ -217,9 +258,10 @@ private static void destroyPile(Pile pile, Player destroyer){ pile.getItems().forEach(i -> display.getWorld().dropItemNaturally(display.getLocation().subtract(0.5, 0.5, 0.5), i)); display.remove(); activePilesByPosition.remove(pile.getPosition()); + return true; } - public static ItemStack takeFromPile(ItemDisplay b, Player destroyer){ + public static ItemStack takeFromPile(ItemDisplay b, @Nonnull Player destroyer){ Pile existingPile = fromEntity(b); if (existingPile == null || !canDestroy(destroyer, existingPile)) return null; ItemDisplay display = existingPile.getDisplay(); @@ -234,8 +276,18 @@ else if (type.getTakeSound() != null) { return item; } + public static void registerSelector(PileTypeSelector selector){ + registeredSelectors.put(selector.getIdentifier(), selector); + } + public static void register(PileType pile){ - registeredPiles.put(pile.getType(), pile); + List types = new ArrayList<>(registeredPiles.values()); + types.add(pile); + types.sort(Comparator.comparingInt(PileType::priority)); + registeredPiles.clear(); + for (PileType type : types) { + registeredPiles.put(type.getType(), type); + } } public static boolean unregister(PileType pile){ @@ -265,10 +317,12 @@ public static int maxAllowedPiles(Player p){ return Math.max(0, def); } - public static boolean canPlacePiles(Player p, boolean sendWarning){ + public static boolean canPlacePiles(Player p){ int limit = maxAllowedPiles(p); boolean allowed = p.isOp() || quantityCounter.getPileQuantities().getOrDefault(p.getUniqueId(), 0) <= limit; - if (sendWarning && !allowed) Utils.sendMessage(p, Piles.getPluginConfig().getString("message_pile_limit_reached", "").replace("%amount%", String.valueOf(limit))); + if (!allowed) { + Utils.sendMessage(p, Piles.getPluginConfig().getString("message_pile_limit_reached", "").replace("%amount%", String.valueOf(limit))); + } return allowed; } @@ -355,4 +409,15 @@ public static boolean togglePlacementBlocked(Player p){ public static Map getRegisteredPiles() { return new HashMap<>(registeredPiles); } + + public static Map getRegisteredSelectors() { + return new HashMap<>(registeredSelectors); + } + + public static PileType typeFromItem(ItemStack item){ + for (PileType pile : registeredPiles.values()){ + if (pile.acceptsItem(item)) return pile; + } + return null; + } } diff --git a/src/main/java/me/athlaeos/piles/domain/Pile.java b/src/main/java/me/athlaeos/piles/domain/Pile.java index f71768e..87648f0 100644 --- a/src/main/java/me/athlaeos/piles/domain/Pile.java +++ b/src/main/java/me/athlaeos/piles/domain/Pile.java @@ -1,15 +1,16 @@ package me.athlaeos.piles.domain; -import me.athlaeos.piles.Piles; import me.athlaeos.piles.piles.PileType; -import org.bukkit.entity.Entity; +import me.athlaeos.piles.utils.Utils; +import org.bukkit.Bukkit; import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; +import java.util.StringJoiner; import java.util.UUID; +import java.util.stream.Collectors; public class Pile { private final String pile; @@ -42,9 +43,16 @@ public List getItems() { return new ArrayList<>(items); } + public String serializeItems() { + StringJoiner joiner = new StringJoiner(""); + for (ItemStack item : items) { + joiner.add(Utils.serialize(item)); + } + return joiner.toString(); + } + public ItemDisplay getDisplay(){ - Entity e = Piles.getInstance().getServer().getEntity(entity); - return e == null ? null : (ItemDisplay) e; + return Bukkit.getEntity(entity) instanceof ItemDisplay display ? display : null; } public void addItem(ItemStack item){ @@ -54,4 +62,9 @@ public void addItem(ItemStack item){ public ItemStack removeItem(){ return items.removeLast(); } + + public boolean isValid() { + ItemDisplay display = getDisplay(); + return display != null && display.isValid(); + } } diff --git a/src/main/java/me/athlaeos/piles/domain/Pos.java b/src/main/java/me/athlaeos/piles/domain/Pos.java index db52b5e..eb2180d 100644 --- a/src/main/java/me/athlaeos/piles/domain/Pos.java +++ b/src/main/java/me/athlaeos/piles/domain/Pos.java @@ -1,11 +1,19 @@ package me.athlaeos.piles.domain; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.Block; + public class Pos { private String world; private int x; private int y; private int z; + public Pos(Block block){ + this(block.getWorld().getName(), block.getX(), block.getY(), block.getZ()); + } + public Pos(String world, int x, int y, int z){ this.world = world; this.x = x; @@ -13,6 +21,11 @@ public Pos(String world, int x, int y, int z){ this.z = z; } + public Block getBlock() { + World world = Bukkit.getWorld(this.world); + return world != null ? world.getBlockAt(x, y, z) : null; + } + public int getX() { return x; } public int getY() { return y; } public int getZ() { return z; } @@ -22,4 +35,15 @@ public Pos(String world, int x, int y, int z){ public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setZ(int z) { this.z = z; } + + @Override + public String toString() { + return world + "," + x + "," + y + "," + z; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + return obj instanceof Pos pos && x == pos.x && y == pos.y && z == pos.z && world.equals(pos.world); + } } diff --git a/src/main/java/me/athlaeos/piles/listeners/PilesListener.java b/src/main/java/me/athlaeos/piles/listeners/PilesListener.java index d5a4b95..9eff1f1 100644 --- a/src/main/java/me/athlaeos/piles/listeners/PilesListener.java +++ b/src/main/java/me/athlaeos/piles/listeners/PilesListener.java @@ -1,7 +1,6 @@ package me.athlaeos.piles.listeners; import me.athlaeos.piles.PileRegistry; -import me.athlaeos.piles.Piles; import me.athlaeos.piles.domain.Pile; import me.athlaeos.piles.utils.Timer; import me.athlaeos.piles.utils.Utils; @@ -10,15 +9,24 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.BlockSupport; import org.bukkit.block.data.Directional; import org.bukkit.entity.FallingBlock; import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.*; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockMultiPlaceEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -27,8 +35,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.RayTraceResult; -import java.util.stream.Collectors; - public class PilesListener implements Listener { private float get8WayDirection(Location direction){ @@ -44,200 +50,211 @@ private float get8WayDirection(Location direction){ } @EventHandler(priority = EventPriority.MONITOR) - public void onInteract(PlayerInteractEvent e){ - if (e.useItemInHand() == Event.Result.DENY || e.getHand() == EquipmentSlot.OFF_HAND || !Timer.isCooldownPassed(e.getPlayer().getUniqueId(), "delay_item_placement")) return; - RayTraceResult result = e.getPlayer().getWorld().rayTraceEntities(e.getPlayer().getEyeLocation(), e.getPlayer().getEyeLocation().getDirection(), 5, 0.3, en -> en instanceof ItemDisplay d && PileRegistry.isPile(d)); - // full destroy or placement of pile - - float direction = get8WayDirection(e.getPlayer().getEyeLocation()); - Timer.setCooldown(e.getPlayer().getUniqueId(), 50, "delay_item_placement"); - if (result != null && result.getHitEntity() != null){ - // interacting with existing pile - if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK){ - ItemStack hand = e.getPlayer().getInventory().getItemInMainHand(); - if (canPlace(e.getPlayer(), result.getHitEntity().getLocation().getBlock(), BlockFace.UP)){ - // do not trust compiler warning saying hand is hand != null is always false, it isn't and this depends on server software like papermc or purpur - if (hand != null && !hand.getType().isAir()) { - if (PileRegistry.placePile(e.getPlayer(), hand, (ItemDisplay) result.getHitEntity(), direction)){ - e.getPlayer().swingMainHand(); - if (e.getPlayer().getGameMode() != GameMode.CREATIVE){ - if (hand.getAmount() == 1) e.getPlayer().getInventory().setItemInMainHand(null); - else hand.setAmount(hand.getAmount() - 1); - } - } - } else { - ItemStack taken = PileRegistry.takeFromPile((ItemDisplay) result.getHitEntity(), e.getPlayer()); - if (taken != null && !taken.getType().isAir()) Utils.addItem(e.getPlayer(), taken, true); - } - e.setCancelled(true); - } - } else if (e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.LEFT_CLICK_BLOCK){ - if (canTake(e.getPlayer(), result.getHitEntity().getLocation().getBlock())) { - PileRegistry.destroyPile(e.getPlayer(), (ItemDisplay) result.getHitEntity()); - e.setCancelled(true); + public void onInteract(PlayerInteractEvent event){ + Block block = event.getClickedBlock(); + Action action = event.getAction(); + Player player = event.getPlayer(); + Location eyeLocation = player.getEyeLocation(); + if (event.useItemInHand() == Event.Result.DENY + || action == Action.PHYSICAL + || event.getHand() == EquipmentSlot.OFF_HAND + || !Timer.isCooldownPassed(player.getUniqueId(), "delay_item_placement")) { + return; + } + + boolean rightClick = event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK; + Timer.setCooldown(event.getPlayer().getUniqueId(), 50, "delay_item_placement"); + RayTraceResult result = player.getWorld().rayTraceEntities(eyeLocation, eyeLocation.getDirection(), 5, 0.3, PileRegistry::isPile); + float direction = get8WayDirection(eyeLocation); + + // interacting with existing pile entity + if (result != null && result.getHitEntity() != null) { + ItemDisplay display = (ItemDisplay) result.getHitEntity(); + block = display.getLocation().getBlock(); + if (rightClick) { + ItemStack held = player.getInventory().getItemInMainHand(); + if (held.getType().isAir()) { + takeFromPile(player, display); + } else { + addOrCreatePile(player, held, block, direction); } + } else { + PileRegistry.destroyPile(player, display); } - } else { - if (e.getClickedBlock() == null) return; - Pile pile = PileRegistry.fromBlock(e.getClickedBlock()); - if (pile == null){ - if (e.getBlockFace() != BlockFace.UP) return; // must be sneaking to place pile - Block b = e.getClickedBlock().getRelative(BlockFace.UP); - if (!b.getRelative(BlockFace.DOWN).getType().isSolid()) return; // block below must be solid - ItemStack hand = e.getPlayer().getInventory().getItemInMainHand(); - if (e.getPlayer().isSneaking() && hand != null && !hand.getType().isAir() && canPlace(e.getPlayer(), e.getClickedBlock(), BlockFace.UP)) { - if (PileRegistry.placePile(e.getPlayer(), hand, b, direction)){ - e.getPlayer().swingMainHand(); - if (e.getPlayer().getGameMode() != GameMode.CREATIVE){ - if (hand.getAmount() == 1) e.getPlayer().getInventory().setItemInMainHand(null); - else hand.setAmount(hand.getAmount() - 1); - } - e.setCancelled(true); - } + event.setCancelled(true); + return; + } + + // If no found entity, rely on the block + if (block == null) return; + + Pile pile = PileRegistry.fromBlock(block); + if (pile == null) { + // No found pile, must be adding/creating + if (!rightClick || !player.isSneaking() || event.getBlockFace() != BlockFace.UP) return; + block = block.getRelative(BlockFace.UP); + if (!block.getRelative(BlockFace.DOWN).getBlockData().isFaceSturdy(BlockFace.UP, BlockSupport.CENTER)) return; // block below must be sturdy + event.setCancelled(addOrCreatePile(player, player.getInventory().getItemInMainHand(), block, direction)); + return; + } + + // interacting with existing pile + ItemDisplay display = pile.getDisplay(); + if (display == null) return; // pile display must exist + + if (rightClick) { + ItemStack hand = player.getInventory().getItemInMainHand(); + if (!hand.getType().isAir()) { + if (player.isSneaking()) { + addOrCreatePile(player, hand, display, direction); + } else { + takeFromPile(player, display); } } else { - // interacting with existing pile - if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK){ - ItemStack hand = e.getPlayer().getInventory().getItemInMainHand(); - if (!hand.getType().isAir()) { - if (!e.getPlayer().isSneaking()){ - if (canPlace(e.getPlayer(), e.getClickedBlock().getLocation().subtract(0, 1, 0).getBlock(), BlockFace.UP)){ - if (PileRegistry.placePile(e.getPlayer(), hand, e.getClickedBlock(), direction)){ - e.getPlayer().swingMainHand(); - if (e.getPlayer().getGameMode() != GameMode.CREATIVE){ - if (hand.getAmount() == 1) e.getPlayer().getInventory().setItemInMainHand(null); - else hand.setAmount(hand.getAmount() - 1); - } - e.setCancelled(true); - } - } - } else if (canTake(e.getPlayer(), e.getClickedBlock())) { - ItemStack taken = PileRegistry.takeFromPile(pile.getDisplay(), e.getPlayer()); - if (taken != null && !taken.getType().isAir()) { - Utils.addItem(e.getPlayer(), taken, true); - e.setCancelled(true); - } - } - } else if (canTake(e.getPlayer(), e.getClickedBlock())) { - ItemStack taken = PileRegistry.takeFromPile(pile.getDisplay(), e.getPlayer()); - if (taken != null && !taken.getType().isAir()) { - Utils.addItem(e.getPlayer(), taken, true); - e.setCancelled(true); - } - } - } else if (e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.LEFT_CLICK_BLOCK){ - if (canTake(e.getPlayer(), e.getClickedBlock())) { - PileRegistry.destroyPile(e.getPlayer(), e.getClickedBlock()); - e.setCancelled(true); - } - } + takeFromPile(player, display); } + } else { + PileRegistry.destroyPile(player, block); } + event.setCancelled(true); } - @EventHandler(priority = EventPriority.MONITOR) + public void addOrCreatePile(Player player, ItemStack held, ItemDisplay display, float direction) { + if (PileRegistry.placePile(player, held, display, direction)){ + onAdded(player, held); + } + } + + public boolean addOrCreatePile(Player player, ItemStack held, Block block, float direction) { + if (!held.getType().isAir() && PileRegistry.placePile(player, held, block, direction)){ + onAdded(player, held); + return true; + } + return false; + } + + public void takeFromPile(Player player, ItemDisplay display) { + ItemStack taken = PileRegistry.takeFromPile(display, player); + if (taken != null && !taken.getType().isAir()) { + Utils.addItem(player, taken, true); + } + } + + private void onAdded(Player player, ItemStack held) { + player.swingMainHand(); + if (player.getGameMode() != GameMode.CREATIVE){ + held.setAmount(held.getAmount() - 1); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onExplosion(BlockExplodeEvent e){ - if (e.isCancelled()) return; - for (Block b : e.blockList()){ - Block above = b.getRelative(BlockFace.UP); - Pile pile = PileRegistry.fromBlock(above); - if (pile != null) PileRegistry.destroyPile(null, above); + for (Block block : e.blockList()){ + Block above = block.getRelative(BlockFace.UP); + if (PileRegistry.fromBlock(above) != null) { + PileRegistry.destroyPile(null, above); + } } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onExplosion(EntityExplodeEvent e){ - if (e.isCancelled()) return; - for (Block b : e.blockList()){ - Block above = b.getRelative(BlockFace.UP); - Pile pile = PileRegistry.fromBlock(above); - if (pile != null) PileRegistry.destroyPile(null, above); + Player cause = null; + if (e.getEntity() instanceof Player player) { + cause = player; + } else if (e.getEntity() instanceof TNTPrimed tnt && tnt.getSource() instanceof Player player) { + cause = player; + } + + for (Block block : e.blockList()){ + Block above = block.getRelative(BlockFace.UP); + if (PileRegistry.fromBlock(above) != null) { + PileRegistry.destroyPile(cause, above); + } } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockBreak(BlockBreakEvent e){ - if (e.isCancelled()) return; Block above = e.getBlock().getRelative(BlockFace.UP); - Pile pile = PileRegistry.fromBlock(above); - if (pile != null) PileRegistry.destroyPile(e.getPlayer(), above); + if (PileRegistry.fromBlock(above) != null) { + PileRegistry.destroyPile(e.getPlayer(), above); + } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onFallBlock(EntityChangeBlockEvent event) { - if (event.isCancelled()) return; if (event.getEntity() instanceof FallingBlock fallingBlock) { - Pile pile = PileRegistry.fromBlock(fallingBlock.getLocation().getBlock()); - if (pile != null) PileRegistry.destroyPile(null, fallingBlock.getLocation().getBlock()); + Block block = fallingBlock.getLocation().getBlock(); + if (PileRegistry.fromBlock(block) != null) { + PileRegistry.destroyPile(null, block); + } } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockForm(BlockFormEvent e){ - if (e.isCancelled()) return; - Pile pile = PileRegistry.fromBlock(e.getBlock()); - if (pile != null && e.getBlock().getType().isOccluding()) PileRegistry.destroyPile(null, e.getBlock()); + Block block = e.getBlock(); + if (block.getType().isOccluding() && PileRegistry.fromBlock(block) != null) { + PileRegistry.destroyPile(null, e.getBlock()); + } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onStructureForm(StructureGrowEvent e){ - if (e.isCancelled()) return; - for (BlockState b : e.getBlocks()){ - Pile pile = PileRegistry.fromBlock(b.getBlock()); - if (pile != null && b.getType().isOccluding()) PileRegistry.destroyPile(null, b.getBlock()); + for (BlockState state : e.getBlocks()){ + Block block = state.getBlock(); + if (state.getType().isOccluding() && PileRegistry.fromBlock(block) != null) { + PileRegistry.destroyPile(null, block); + } } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPistonMove(BlockPistonExtendEvent e){ - if (e.isCancelled()) return; - if (e.getBlock().getBlockData() instanceof Directional d){ - for (Block b : e.getBlocks().stream().map(b -> b.getRelative(d.getFacing())).collect(Collectors.toSet())){ - Pile pile = PileRegistry.fromBlock(b); - if (pile != null && b.getType().isOccluding()) PileRegistry.destroyPile(null, b); + if (!(e.getBlock().getBlockData() instanceof Directional d)) { + return; + } + + BlockFace face = d.getFacing(); + for (Block block : e.getBlocks()) { + block = block.getRelative(face); + if (block.getType().isOccluding() && PileRegistry.fromBlock(block) != null) { + PileRegistry.destroyPile(null, block); } } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPistonMove(BlockPistonRetractEvent e){ - if (e.isCancelled()) return; - if (e.getBlock().getBlockData() instanceof Directional d){ - for (Block b : e.getBlocks().stream().map(b -> b.getRelative(d.getFacing().getOppositeFace())).collect(Collectors.toSet())){ - Pile pile = PileRegistry.fromBlock(b); - if (pile != null && b.getType().isOccluding()) PileRegistry.destroyPile(null, b); + if (!(e.getBlock().getBlockData() instanceof Directional d)) { + return; + } + + BlockFace face = d.getFacing().getOppositeFace(); + for (Block block : e.getBlocks()) { + block = block.getRelative(face); + if (block.getType().isOccluding() && PileRegistry.fromBlock(block) != null) { + PileRegistry.destroyPile(null, block); } } } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockPlace(BlockPlaceEvent e){ - if (e.isCancelled()) return; - Pile pile = PileRegistry.fromBlock(e.getBlock()); - if (pile != null && e.getBlock().getType().isOccluding()) e.setCancelled(true); // do not place solid blocks on piles + Block block = e.getBlock(); + if (block.getType().isOccluding() && PileRegistry.fromBlock(block) != null) { + e.setCancelled(true); // do not place solid blocks on piles + } } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockPlace(BlockMultiPlaceEvent e){ - if (e.isCancelled()) return; for (BlockState state : e.getReplacedBlockStates()) { - Pile pile = PileRegistry.fromBlock(state.getBlock()); - if (pile != null) { + if (PileRegistry.fromBlock(state.getBlock()) != null) { e.setCancelled(true); // do not place blocks on piles return; } } } - - private boolean canPlace(Player player, Block against, BlockFace face){ - BlockPlaceEvent event = new BlockPlaceEvent(against.getRelative(face), against.getState(), against, player.getInventory().getItemInMainHand(), player, true, EquipmentSlot.HAND); - Piles.getInstance().getServer().getPluginManager().callEvent(event); - return !event.isCancelled(); - } - - private boolean canTake(Player player, Block from){ - BlockBreakEvent event = new BlockBreakEvent(from, player); - Piles.getInstance().getServer().getPluginManager().callEvent(event); - return !event.isCancelled(); - } }