diff --git a/plugin/src/main/java/com/Acrobot/ChestShop/ChestShop.java b/plugin/src/main/java/com/Acrobot/ChestShop/ChestShop.java index 86eb1fd5b..545332180 100644 --- a/plugin/src/main/java/com/Acrobot/ChestShop/ChestShop.java +++ b/plugin/src/main/java/com/Acrobot/ChestShop/ChestShop.java @@ -10,6 +10,7 @@ import com.Acrobot.ChestShop.Configuration.Messages; import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Database.Migrations; +import com.Acrobot.ChestShop.Events.StockUpdateEvent; import com.Acrobot.ChestShop.Listeners.Block.BlockPlace; import com.Acrobot.ChestShop.Listeners.Block.Break.ChestBreak; import com.Acrobot.ChestShop.Listeners.Block.Break.SignBreak; @@ -41,6 +42,7 @@ import com.Acrobot.ChestShop.Listeners.PreTransaction.PermissionChecker; import com.Acrobot.ChestShop.Listeners.ShopRemoval.ShopRefundListener; import com.Acrobot.ChestShop.Listeners.ShopRemoval.ShopRemovalLogger; +import com.Acrobot.ChestShop.Listeners.StockUpdateListener; import com.Acrobot.ChestShop.Logging.FileFormatter; import com.Acrobot.ChestShop.Metadata.ItemDatabase; import com.Acrobot.ChestShop.Signs.RestrictedSign; @@ -53,7 +55,6 @@ import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; -import com.google.common.reflect.ClassPath; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -372,9 +373,12 @@ private void registerEvents() { registerEvent(new RestrictedSign()); - if (!Properties.TURN_OFF_HOPPER_PROTECTION || Properties.USE_STOCK_COUNTER) { - registerEvent(new ItemMoveListener()); + if (Properties.USE_STOCK_COUNTER) { + registerEvent(new StockUpdateListener()); } + + // Register after init, so that we can check if someone is listening. + getServer().getScheduler().runTask(this, this::registerStockListeners); } private void registerShopRemovalEvents() { @@ -435,7 +439,6 @@ private void registerModules() { registerEvent(new DiscountModule()); registerEvent(new MetricsModule()); registerEvent(new PriceRestrictionModule()); - registerEvent(new StockCounterModule()); registerEconomicalModules(); } @@ -445,6 +448,15 @@ private void registerEconomicalModules() { registerEvent(new TaxModule()); } + private void registerStockListeners() { + if ( StockUpdateEvent.hasHandlers()) { + registerEvent(new ItemMoveListener()); + registerEvent(new StockCounterModule()); + } else if ( !Properties.TURN_OFF_HOPPER_PROTECTION ) { + registerEvent(new ItemMoveListener()); + } + } + private void registerVersionedAdapters() { // Search jar file for version adapters try (JarFile jarFile = new JarFile(this.getFile())) { diff --git a/plugin/src/main/java/com/Acrobot/ChestShop/Events/StockUpdateEvent.java b/plugin/src/main/java/com/Acrobot/ChestShop/Events/StockUpdateEvent.java new file mode 100644 index 000000000..14c7c0454 --- /dev/null +++ b/plugin/src/main/java/com/Acrobot/ChestShop/Events/StockUpdateEvent.java @@ -0,0 +1,51 @@ +package com.Acrobot.ChestShop.Events; + +import org.bukkit.block.Sign; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Represents a state after a shop's stock changes + * + * @author bjorn-out + */ +public class StockUpdateEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + + private final int stock; + private final Sign sign; + + public StockUpdateEvent(int stock, Sign sign) { + this.stock = stock; + this.sign = sign; + } + + /** + * @return Stock available (number of items) + */ + public int getStock() { + return stock; + } + + /** + * @return Shop's sign + */ + public Sign getSign() { + return sign; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public static boolean hasHandlers() { + return handlers.getRegisteredListeners().length > 0; + } + + @Override + @NotNull + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Item/ItemMoveListener.java b/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Item/ItemMoveListener.java index fe7a1bfb8..ea28d8430 100644 --- a/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Item/ItemMoveListener.java +++ b/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Item/ItemMoveListener.java @@ -1,8 +1,10 @@ package com.Acrobot.ChestShop.Listeners.Item; +import com.Acrobot.ChestShop.ChestShop; import com.Acrobot.ChestShop.Configuration.Properties; -import com.Acrobot.ChestShop.Listeners.Modules.StockCounterModule; +import com.Acrobot.ChestShop.Events.StockUpdateEvent; import com.Acrobot.ChestShop.Signs.ChestShopSign; +import org.bukkit.Bukkit; import org.bukkit.block.BlockState; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -11,6 +13,7 @@ import org.bukkit.inventory.InventoryHolder; import static com.Acrobot.Breeze.Utils.ImplementationAdapter.getHolder; +import static com.Acrobot.ChestShop.Listeners.Modules.StockCounterModule.fireStockUpdateEvents; /** * @author Acrobot @@ -28,8 +31,10 @@ public static void onItemMove(InventoryMoveItemEvent event) { return; } } - if (Properties.USE_STOCK_COUNTER && ChestShopSign.isShopBlock(destinationHolder)) { - StockCounterModule.updateCounterOnItemMoveEvent(event.getItem(), destinationHolder); + + if (StockUpdateEvent.hasHandlers() && ChestShopSign.isShopBlock(destinationHolder)) { + Bukkit.getScheduler().runTask(ChestShop.getPlugin(), () -> + fireStockUpdateEvents(event.getDestination())); } } diff --git a/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/StockCounterModule.java b/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/StockCounterModule.java index 1e3ca9d7a..2154cdb0f 100644 --- a/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/StockCounterModule.java +++ b/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/StockCounterModule.java @@ -1,17 +1,14 @@ package com.Acrobot.ChestShop.Listeners.Modules; import com.Acrobot.Breeze.Utils.InventoryUtil; -import com.Acrobot.Breeze.Utils.MaterialUtil; -import com.Acrobot.Breeze.Utils.QuantityUtil; import com.Acrobot.ChestShop.ChestShop; -import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Events.ItemParseEvent; import com.Acrobot.ChestShop.Events.PreShopCreationEvent; +import com.Acrobot.ChestShop.Events.StockUpdateEvent; import com.Acrobot.ChestShop.Events.TransactionEvent; import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.Acrobot.ChestShop.Utils.uBlock; import org.bukkit.Bukkit; -import org.bukkit.block.Block; import org.bukkit.block.Container; import org.bukkit.block.Sign; import org.bukkit.event.EventHandler; @@ -23,165 +20,55 @@ import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import java.util.IllegalFormatException; - import static com.Acrobot.Breeze.Utils.ImplementationAdapter.getHolder; -import static com.Acrobot.ChestShop.Signs.ChestShopSign.QUANTITY_LINE; +import static org.bukkit.Bukkit.getServer; /** * @author bricefrisco */ public class StockCounterModule implements Listener { - private static final String PRICE_LINE_WITH_COUNT = "Q %d : C %d"; - @EventHandler(priority = EventPriority.HIGH) public static void onPreShopCreation(PreShopCreationEvent event) { - int quantity; - try { - quantity = ChestShopSign.getQuantity(event.getSignLines()); - } catch (IllegalArgumentException invalidQuantity) { - return; - } - - if (QuantityUtil.quantityLineContainsCounter(ChestShopSign.getQuantityLine(event.getSignLines()))) { - event.setSignLine(QUANTITY_LINE, Integer.toString(quantity)); - } - - if (!Properties.USE_STOCK_COUNTER - || (Properties.FORCE_UNLIMITED_ADMIN_SHOP && ChestShopSign.isAdminShop(event.getSignLines()))) { - return; - } + if (!StockUpdateEvent.hasHandlers()) { return; } + Container container = uBlock.findConnectedContainer(event.getSign()); + if (container == null) { return; } - if (Properties.MAX_SHOP_AMOUNT > 99999) { - ChestShop.getBukkitLogger().warning("Stock counter cannot be used if MAX_SHOP_AMOUNT is over 5 digits"); - return; - } - - ItemStack itemTradedByShop = determineItemTradedByShop(ChestShopSign.getItem(event.getSignLines())); - if (itemTradedByShop != null) { - Container container = uBlock.findConnectedContainer(event.getSign()); - if (container != null) { - event.setSignLine(QUANTITY_LINE, getQuantityLineWithCounter(quantity, itemTradedByShop, container.getInventory())); - } - } + Bukkit.getScheduler().runTask(ChestShop.getPlugin(), () -> + fireStockUpdateEvents(container.getInventory())); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public static void onInventoryClose(InventoryCloseEvent event) { + if (!StockUpdateEvent.hasHandlers()) { return; } + if (event.getInventory().getType() == InventoryType.ENDER_CHEST || event.getInventory().getLocation() == null) { return; } InventoryHolder holder = getHolder(event.getInventory(), false); - if (!uBlock.couldBeShopContainer(holder)) { - return; - } - - for (Sign shopSign : uBlock.findConnectedShopSigns(holder)) { - if (!Properties.USE_STOCK_COUNTER - || (Properties.FORCE_UNLIMITED_ADMIN_SHOP && ChestShopSign.isAdminShop(shopSign))) { - if (QuantityUtil.quantityLineContainsCounter(ChestShopSign.getQuantityLine(shopSign))) { - removeCounterFromQuantityLine(shopSign); - } - continue; - } - - if (Properties.MAX_SHOP_AMOUNT > 99999) { - ChestShop.getBukkitLogger().warning("Stock counter cannot be used if MAX_SHOP_AMOUNT is over 5 digits"); - if (QuantityUtil.quantityLineContainsCounter(ChestShopSign.getQuantityLine(shopSign))) { - removeCounterFromQuantityLine(shopSign); - } - return; - } + if (!uBlock.couldBeShopContainer(holder)) { return; } - updateCounterOnQuantityLine(shopSign, event.getInventory()); - } + fireStockUpdateEvents(event.getInventory()); } - @EventHandler(priority = EventPriority.HIGH) + @EventHandler(priority = EventPriority.MONITOR) public static void onTransaction(final TransactionEvent event) { - String quantityLine = ChestShopSign.getQuantityLine(event.getSign()); - if (!Properties.USE_STOCK_COUNTER) { - if (QuantityUtil.quantityLineContainsCounter(quantityLine)) { - removeCounterFromQuantityLine(event.getSign()); - } - return; - } - - if (Properties.MAX_SHOP_AMOUNT > 99999) { - ChestShop.getBukkitLogger().warning("Stock counter cannot be used if MAX_SHOP_AMOUNT is over 5 digits"); - if (QuantityUtil.quantityLineContainsCounter(quantityLine)) { - removeCounterFromQuantityLine(event.getSign()); - } - return; - } - - if (Properties.FORCE_UNLIMITED_ADMIN_SHOP && ChestShopSign.isAdminShop(event.getSign())) { - return; - } - - for (Sign shopSign : uBlock.findConnectedShopSigns( getHolder(event.getOwnerInventory(), false))) { - updateCounterOnQuantityLine(shopSign, event.getOwnerInventory()); - } + if (!StockUpdateEvent.hasHandlers()) { return; } + fireStockUpdateEvents(event.getOwnerInventory()); } - /** - * Update the stock counter on the sign's quantity line - * @param sign The sign to update - * @param chestShopInventory The inventory to search in - * @param extraItems The extra items to add in the search - */ - public static void updateCounterOnQuantityLine(Sign sign, Inventory chestShopInventory, ItemStack... extraItems) { - ItemStack itemTradedByShop = determineItemTradedByShop(sign); - if (itemTradedByShop == null) { - return; - } - - int quantity; - try { - quantity = ChestShopSign.getQuantity(sign); - } catch (IllegalFormatException invalidQuantity) { - return; - } - - int numTradedItemsInChest = InventoryUtil.getAmount(itemTradedByShop, chestShopInventory); - - for (ItemStack extraStack : extraItems) { - if (!MaterialUtil.equals(extraStack, itemTradedByShop)) { - continue; + public static void fireStockUpdateEvents(Inventory inventory) { + for (Sign sign : uBlock.findConnectedShopSigns( getHolder(inventory, false))) { + ItemStack itemTradedByShop = determineItemTradedByShop(sign); + if (itemTradedByShop == null) { + return; } + int stock = InventoryUtil.getAmount(itemTradedByShop, inventory); - numTradedItemsInChest += extraStack.getAmount(); - } - - sign.setLine(QUANTITY_LINE, String.format(PRICE_LINE_WITH_COUNT, quantity, numTradedItemsInChest)); - sign.update(true); - } - - public static void updateCounterOnItemMoveEvent(ItemStack toAdd, InventoryHolder destinationHolder) { - Block shopBlock = ChestShopSign.getShopBlock(destinationHolder); - Sign connectedSign = uBlock.getConnectedSign(shopBlock); - - updateCounterOnQuantityLine(connectedSign, destinationHolder.getInventory(), toAdd); - } - - public static void removeCounterFromQuantityLine(Sign sign) { - int quantity; - try { - quantity = ChestShopSign.getQuantity(sign); - } catch (IllegalFormatException invalidQuantity) { - return; + StockUpdateEvent updateEvent = new StockUpdateEvent(stock, sign); + getServer().getPluginManager().callEvent(updateEvent); } - - sign.setLine(QUANTITY_LINE, Integer.toString(quantity)); - sign.update(true); - } - - public static String getQuantityLineWithCounter(int amount, ItemStack itemTransacted, Inventory chestShopInventory) { - int numTransactionItemsInChest = InventoryUtil.getAmount(itemTransacted, chestShopInventory); - - return String.format(PRICE_LINE_WITH_COUNT, amount, numTransactionItemsInChest); } public static ItemStack determineItemTradedByShop(Sign sign) { diff --git a/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/StockUpdateListener.java b/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/StockUpdateListener.java new file mode 100644 index 000000000..0972f6fd7 --- /dev/null +++ b/plugin/src/main/java/com/Acrobot/ChestShop/Listeners/StockUpdateListener.java @@ -0,0 +1,55 @@ +package com.Acrobot.ChestShop.Listeners; + +import com.Acrobot.Breeze.Utils.QuantityUtil; +import com.Acrobot.ChestShop.ChestShop; +import com.Acrobot.ChestShop.Configuration.Properties; +import com.Acrobot.ChestShop.Events.StockUpdateEvent; +import com.Acrobot.ChestShop.Signs.ChestShopSign; +import org.bukkit.block.Sign; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import java.util.IllegalFormatException; + +import static com.Acrobot.ChestShop.Signs.ChestShopSign.QUANTITY_LINE; + +/** + * @author bjorn-out + */ +public class StockUpdateListener implements Listener { + private static final String PRICE_LINE_WITH_COUNT = "Q %d : C %d"; + + @EventHandler(priority = EventPriority.LOW) + public static void onStockUpdate(StockUpdateEvent event) { + int quantity; + try { + quantity = ChestShopSign.getQuantity(event.getSign()); + } catch (IllegalFormatException invalidQuantity) { + return; + } + + if (Properties.FORCE_UNLIMITED_ADMIN_SHOP && ChestShopSign.isAdminShop(event.getSign().getLines())) { + removeCounter(event.getSign(), quantity); + return; + } + + if (Properties.MAX_SHOP_AMOUNT > 99999) { + ChestShop.getBukkitLogger().warning("Stock counter cannot be used if MAX_SHOP_AMOUNT is over 5 digits"); + removeCounter(event.getSign(), quantity); + return; + } + + event.getSign().setLine(QUANTITY_LINE, String.format(PRICE_LINE_WITH_COUNT, quantity, event.getStock())); + event.getSign().update(true); + } + + private static void removeCounter(Sign sign, int quantity) { + if (!QuantityUtil.quantityLineContainsCounter(ChestShopSign.getQuantityLine(sign.getLines()))) { + return; + } + + sign.setLine(QUANTITY_LINE, Integer.toString(quantity)); + sign.update(true); + } +}