diff --git a/.gitignore b/.gitignore index d717d44..a3fabf2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ target/ .project .factorypath -/.vscode/ \ No newline at end of file +/.vscode/ + +*.DS_Store \ No newline at end of file diff --git a/plugin/pom.xml b/plugin/pom.xml index bcb52a4..eaa7c1a 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -68,10 +68,6 @@ org.codemc.worldguardwrapper worldguardwrapper - - org.bstats - bstats-bukkit - com.zaxxer HikariCP @@ -83,6 +79,12 @@ org.inventivetalent reflectionhelper + + + junit + junit + + de.epiceric diff --git a/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java b/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java index f1efb39..b64d4bd 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java +++ b/plugin/src/main/java/de/epiceric/shopchest/ShopChest.java @@ -1,29 +1,33 @@ package de.epiceric.shopchest; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; - import com.palmergames.bukkit.towny.Towny; +import com.plotsquared.core.PlotSquared; import com.wasteofplastic.askyblock.ASkyBlock; - +import de.epiceric.shopchest.command.ShopCommand; +import de.epiceric.shopchest.config.Config; +import de.epiceric.shopchest.config.hologram.HologramFormat; +import de.epiceric.shopchest.event.ShopInitializedEvent; +import de.epiceric.shopchest.external.BentoBoxShopFlag; +import de.epiceric.shopchest.external.PlotSquaredOldShopFlag; +import de.epiceric.shopchest.external.PlotSquaredShopFlag; +import de.epiceric.shopchest.external.WorldGuardShopFlag; +import de.epiceric.shopchest.external.listeners.*; +import de.epiceric.shopchest.language.LanguageUtils; +import de.epiceric.shopchest.listeners.BentoBoxListener; +import de.epiceric.shopchest.listeners.WorldGuardListener; +import de.epiceric.shopchest.listeners.*; import de.epiceric.shopchest.nms.Platform; import de.epiceric.shopchest.nms.reflection.PlatformImpl; import de.epiceric.shopchest.nms.reflection.ShopChestDebug; -import org.bstats.bukkit.Metrics; -import org.bstats.charts.AdvancedPie; -import org.bstats.charts.SimplePie; +import de.epiceric.shopchest.sql.Database; +import de.epiceric.shopchest.sql.MySQL; +import de.epiceric.shopchest.sql.SQLite; +import de.epiceric.shopchest.utils.*; +import de.epiceric.shopchest.utils.UpdateChecker.UpdateCheckerResult; +import fr.xephi.authme.AuthMe; +import me.ryanhamshire.GriefPrevention.GriefPrevention; +import me.wiefferink.areashop.AreaShop; +import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; @@ -33,53 +37,24 @@ import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; import org.codemc.worldguardwrapper.WorldGuardWrapper; - -import de.epiceric.shopchest.command.ShopCommand; -import de.epiceric.shopchest.config.Config; -import de.epiceric.shopchest.config.hologram.HologramFormat; -import de.epiceric.shopchest.event.ShopInitializedEvent; -import de.epiceric.shopchest.external.BentoBoxShopFlag; -import de.epiceric.shopchest.external.PlotSquaredOldShopFlag; -import de.epiceric.shopchest.external.PlotSquaredShopFlag; -import de.epiceric.shopchest.external.WorldGuardShopFlag; -import de.epiceric.shopchest.external.listeners.ASkyBlockListener; -import de.epiceric.shopchest.external.listeners.GriefPreventionListener; -import de.epiceric.shopchest.external.listeners.IslandWorldListener; -import de.epiceric.shopchest.external.listeners.PlotSquaredListener; -import de.epiceric.shopchest.external.listeners.TownyListener; -import de.epiceric.shopchest.external.listeners.USkyBlockListener; -import de.epiceric.shopchest.language.LanguageUtils; -import de.epiceric.shopchest.listeners.AreaShopListener; -import de.epiceric.shopchest.listeners.BentoBoxListener; -import de.epiceric.shopchest.listeners.BlockExplodeListener; -import de.epiceric.shopchest.listeners.ChestProtectListener; -import de.epiceric.shopchest.listeners.CreativeModeListener; -import de.epiceric.shopchest.listeners.NotifyPlayerOnJoinListener; -import de.epiceric.shopchest.listeners.ShopInteractListener; -import de.epiceric.shopchest.listeners.ShopItemListener; -import de.epiceric.shopchest.listeners.ShopUpdateListener; -import de.epiceric.shopchest.listeners.WorldGuardListener; -import de.epiceric.shopchest.shop.Shop; -import de.epiceric.shopchest.shop.Shop.ShopType; -import de.epiceric.shopchest.sql.Database; -import de.epiceric.shopchest.sql.MySQL; -import de.epiceric.shopchest.sql.SQLite; -import de.epiceric.shopchest.utils.Callback; -import de.epiceric.shopchest.utils.ClickType; -import de.epiceric.shopchest.utils.Permissions; -import de.epiceric.shopchest.utils.ShopUpdater; -import de.epiceric.shopchest.utils.ShopUtils; -import de.epiceric.shopchest.utils.UpdateChecker; -import de.epiceric.shopchest.utils.UpdateChecker.UpdateCheckerResult; -import de.epiceric.shopchest.utils.Utils; -import fr.xephi.authme.AuthMe; -import me.ryanhamshire.GriefPrevention.GriefPrevention; -import me.wiefferink.areashop.AreaShop; -import net.milkbowl.vault.economy.Economy; import pl.islandworld.IslandWorld; import us.talabrek.ultimateskyblock.api.uSkyBlockAPI; import world.bentobox.bentobox.BentoBox; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + public class ShopChest extends JavaPlugin { private static ShopChest instance; @@ -235,7 +210,6 @@ public class ShopChest extends JavaPlugin { 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); loadExternalPlugins(); - loadMetrics(); initDatabase(); checkForUpdates(); registerListeners(); @@ -361,30 +335,6 @@ public class ShopChest extends JavaPlugin { } } - private void loadMetrics() { - debug("Initializing Metrics..."); - - Metrics metrics = new Metrics(this, 1726); - metrics.addCustomChart(new SimplePie("creative_setting", () -> Config.creativeSelectItem ? "Enabled" : "Disabled")); - metrics.addCustomChart(new SimplePie("database_type", () -> Config.databaseType.toString())); - metrics.addCustomChart(new AdvancedPie("shop_type", () -> { - int normal = 0; - int admin = 0; - - for (Shop shop : shopUtils.getShops()) { - if (shop.getShopType() == ShopType.NORMAL) normal++; - else if (shop.getShopType() == ShopType.ADMIN) admin++; - } - - Map result = new HashMap<>(); - - result.put("Admin", admin); - result.put("Normal", normal); - - return result; - })); - } - private void initDatabase() { if (Config.databaseType == Database.DatabaseType.SQLite) { debug("Using database type: SQLite"); @@ -395,12 +345,9 @@ public class ShopChest extends JavaPlugin { getLogger().info("Using MySQL"); database = new MySQL(this); if (Config.databaseMySqlPingInterval > 0) { - Bukkit.getScheduler().runTaskTimer(this, new Runnable() { - @Override - public void run() { - if (database instanceof MySQL) { - ((MySQL) database).ping(); - } + Bukkit.getScheduler().runTaskTimer(this, () -> { + if (database instanceof MySQL) { + ((MySQL) database).ping(); } }, Config.databaseMySqlPingInterval * 20L, Config.databaseMySqlPingInterval * 20L); } @@ -484,8 +431,11 @@ public class ShopChest extends JavaPlugin { getServer().getPluginManager().registerEvents(new GriefPreventionListener(this), this); if (hasIslandWorld()) getServer().getPluginManager().registerEvents(new IslandWorldListener(this), this); - if (hasPlotSquared()) - getServer().getPluginManager().registerEvents(new PlotSquaredListener(this), this); + if (hasPlotSquared()) { + PlotSquaredListener psListener = new PlotSquaredListener(this); + getServer().getPluginManager().registerEvents(psListener, this); + PlotSquared.get().getEventDispatcher().registerListener(psListener); + } if (hasTowny()) getServer().getPluginManager().registerEvents(new TownyListener(this), this); if (hasUSkyBlock()) diff --git a/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommand.java b/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommand.java index 0a0b90b..4e70349 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommand.java +++ b/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommand.java @@ -155,6 +155,17 @@ public class ShopCommand { } }); + addSubCommand(new ShopSubCommand("value", true, executor, tabCompleter) { + @Override + public String getHelpMessage(CommandSender sender) { + if (sender.hasPermission(Permissions.CONFIG)) { + return LanguageUtils.getMessage(Message.COMMAND_DESC_VALUE, cmdReplacement); + } else { + return ""; + } + } + }); + register(); commandCreated = true; } diff --git a/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java b/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java index c579347..7aeef5f 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java +++ b/plugin/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java @@ -2,12 +2,19 @@ package de.epiceric.shopchest.command; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.GameMode; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.command.Command; @@ -133,6 +140,8 @@ class ShopCommandExecutor implements CommandExecutor { new Replacement(Placeholder.AMOUNT, String.valueOf(shopUtils.getShopAmount(p))))); } else if (subCommand.getName().equalsIgnoreCase("open")) { open(p); + } else if (subCommand.getName().equalsIgnoreCase("value")) { + value(p); } else { return false; } @@ -437,26 +446,31 @@ class ShopCommandExecutor implements CommandExecutor { } } - double creationPrice = (shopType == Shop.ShopType.NORMAL) ?Config.shopCreationPriceNormal :Config.shopCreationPriceAdmin; - if (creationPrice > 0) { - if (plugin.getEconomy().getBalance(p, p.getWorld().getName()) < creationPrice) { - p.sendMessage(LanguageUtils.getMessage(Message.SHOP_CREATE_NOT_ENOUGH_MONEY, new Replacement(Placeholder.CREATION_PRICE, String.valueOf(creationPrice)))); - plugin.debug(p.getName() + " can not pay the creation price"); - return; + CompletableFuture.runAsync(() -> { + double creationPrice = (shopType == Shop.ShopType.NORMAL) ?Config.shopCreationPriceNormal :Config.shopCreationPriceAdmin; + if (creationPrice > 0) { + if (plugin.getEconomy().getBalance(p, p.getWorld().getName()) < creationPrice) { + p.sendMessage(LanguageUtils.getMessage(Message.SHOP_CREATE_NOT_ENOUGH_MONEY, new Replacement(Placeholder.CREATION_PRICE, String.valueOf(creationPrice)))); + plugin.debug(p.getName() + " can not pay the creation price"); + return; + } } - } - ShopProduct product = new ShopProduct(itemStack, amount); - ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, product, null, buyPrice, sellPrice, shopType)); - Bukkit.getPluginManager().callEvent(event); + ShopProduct product = new ShopProduct(itemStack, amount); + ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, product, null, buyPrice, sellPrice, shopType)); - if (!event.isCancelled()) { - ClickType.setPlayerClickType(p, new CreateClickType(product, buyPrice, sellPrice, shopType)); - plugin.debug(p.getName() + " can now click a chest"); - p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_CREATE)); - } else { - plugin.debug("Shop pre create event cancelled"); - } + Bukkit.getScheduler().runTask(plugin, () -> { + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + ClickType.setPlayerClickType(p, new CreateClickType(product, buyPrice, sellPrice, shopType)); + plugin.debug(p.getName() + " can now click a chest"); + p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_CREATE)); + } else { + plugin.debug("Shop pre create event cancelled"); + } + }); + }); } /** @@ -516,6 +530,57 @@ class ShopCommandExecutor implements CommandExecutor { ClickType.setPlayerClickType(p, new ClickType(ClickType.EnumClickType.OPEN)); } + private void value(final Player p) { + ItemStack item = p.getInventory().getItemInMainHand(); + + if (item.getType() == Material.AIR || item.getType() == Material.CAVE_AIR || item.getType() == Material.VOID_AIR) { + return; + } + + AtomicInteger buyCount = new AtomicInteger(); + AtomicInteger sellCount = new AtomicInteger(); + AtomicReference buyPrice = new AtomicReference<>(0d); + AtomicReference sellPrice = new AtomicReference<>(0d); + Set sellers = new HashSet<>(); + + plugin.getShopUtils().getShops().forEach(shop -> { + if (!shop.hasItem()) { + return; + } + if (sellers.contains(shop.getVendor().getUniqueId())) { + return; + } + if (!Utils.isItemSimilar(shop.getProduct().getItemStack(), item)) { + return; + } + if (shop.getShopType().equals(ShopType.ADMIN)) { + return; + } + + sellers.add(shop.getVendor().getUniqueId()); + if (shop.getBuyPrice() != 0) { + buyPrice.updateAndGet(v -> v + shop.getBuyPrice() / shop.getProduct().getAmount()); + buyCount.getAndIncrement(); + } + if (shop.getSellPrice() != 0) { + sellPrice.updateAndGet(v -> v + shop.getSellPrice() / shop.getProduct().getAmount()); + sellCount.getAndIncrement(); + } + }); + + if (buyCount.get() == 0) { + p.sendMessage(LanguageUtils.getMessage(Message.VALUE_NO_SHOPS_BUY)); + } else { + p.sendMessage(LanguageUtils.getMessage(Message.VALUE_OF_ITEM_BUY, new Replacement(Placeholder.AMOUNT, sellers.size()), new Replacement(Placeholder.BUY_PRICE, buyPrice.get() / (double) buyCount.get()))); + } + if (sellCount.get() == 0) { + p.sendMessage(LanguageUtils.getMessage(Message.VALUE_NO_SHOPS_SELL)); + } else { + p.sendMessage(LanguageUtils.getMessage(Message.VALUE_OF_ITEM_SELL, new Replacement(Placeholder.AMOUNT, sellers.size()), new Replacement(Placeholder.SELL_PRICE, sellPrice.get() / (double) sellCount.get()))); + } + + } + private boolean changeConfig(CommandSender sender, String[] args) { plugin.debug(sender.getName() + " is changing the configuration"); diff --git a/plugin/src/main/java/de/epiceric/shopchest/config/Config.java b/plugin/src/main/java/de/epiceric/shopchest/config/Config.java index 21e9cb3..bfbffc9 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/config/Config.java +++ b/plugin/src/main/java/de/epiceric/shopchest/config/Config.java @@ -8,7 +8,9 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.inventory.ItemStack; @@ -61,6 +63,8 @@ public class Config { **/ public static List areashopRemoveShopEvents; + public static Map shopTaxes; + /** * The hostname used in ShopChest's MySQL database **/ @@ -468,6 +472,7 @@ public class Config { databaseType = Database.DatabaseType.valueOf(plugin.getConfig().getString("database.type")); minimumPrices = (plugin.getConfig().getConfigurationSection("minimum-prices") == null) ? new HashSet() : plugin.getConfig().getConfigurationSection("minimum-prices").getKeys(true); maximumPrices = (plugin.getConfig().getConfigurationSection("maximum-prices") == null) ? new HashSet() : plugin.getConfig().getConfigurationSection("maximum-prices").getKeys(true); + shopTaxes = (plugin.getConfig().getConfigurationSection("shop-taxes") == null ? Map.of("default", 0d) : plugin.getConfig().getConfigurationSection("shop-taxes").getKeys(true).stream().collect(Collectors.toMap(i -> i, i -> plugin.getConfig().getConfigurationSection("shop-taxes").getDouble(i)))); allowDecimalsInPrice = plugin.getConfig().getBoolean("allow-decimals-in-price"); allowBrokenItems = plugin.getConfig().getBoolean("allow-broken-items"); autoCalculateItemAmount = (allowDecimalsInPrice && plugin.getConfig().getBoolean("auto-calculate-item-amount")); diff --git a/plugin/src/main/java/de/epiceric/shopchest/config/LanguageConfiguration.java b/plugin/src/main/java/de/epiceric/shopchest/config/LanguageConfiguration.java index 7bc3f3c..3125157 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/config/LanguageConfiguration.java +++ b/plugin/src/main/java/de/epiceric/shopchest/config/LanguageConfiguration.java @@ -43,10 +43,8 @@ public class LanguageConfiguration extends FileConfiguration { @Override public String getString(String path, String def) { - for (String key : values.keySet()) { - if (key.equals(path)) { - return values.get(key); - } + if (values.containsKey(path)) { + return values.get(path); } values.put(path, def); diff --git a/plugin/src/main/java/de/epiceric/shopchest/external/BentoBoxShopFlag.java b/plugin/src/main/java/de/epiceric/shopchest/external/BentoBoxShopFlag.java index de47c60..2e0333e 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/external/BentoBoxShopFlag.java +++ b/plugin/src/main/java/de/epiceric/shopchest/external/BentoBoxShopFlag.java @@ -8,18 +8,33 @@ import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.managers.RanksManager; public class BentoBoxShopFlag { - public static final Flag SHOP_FLAG = new Flag.Builder("CREATE_SHOPS", Material.CHEST) + public static final Flag SHOP_CHEST_FLAG = new Flag.Builder("CREATE_SHOPS", Material.CHEST) + .type(Flag.Type.PROTECTION) + .mode(Flag.Mode.BASIC) + .defaultRank(RanksManager.TRUSTED_RANK) + .build(); + public static final Flag SHOP_TRAPPED_CHEST_FLAG = new Flag.Builder("CREATE_SHOPS", Material.TRAPPED_CHEST) + .type(Flag.Type.PROTECTION) + .mode(Flag.Mode.BASIC) + .defaultRank(RanksManager.TRUSTED_RANK) + .build(); + public static final Flag SHOP_SHULKER_BOX_FLAG = new Flag.Builder("CREATE_SHOPS", Material.SHULKER_BOX) + .type(Flag.Type.PROTECTION) + .mode(Flag.Mode.BASIC) + .defaultRank(RanksManager.TRUSTED_RANK) + .build(); + public static final Flag SHOP_BARREL_FLAG = new Flag.Builder("CREATE_SHOPS", Material.BARREL) .type(Flag.Type.PROTECTION) .mode(Flag.Mode.BASIC) .defaultRank(RanksManager.TRUSTED_RANK) .build(); public static void register(ShopChest plugin) { - if (BentoBox.getInstance().getFlagsManager().registerFlag(SHOP_FLAG)) { - plugin.debug("Registered BentoBox shop flag"); + if (BentoBox.getInstance().getFlagsManager().registerFlag(SHOP_CHEST_FLAG) && BentoBox.getInstance().getFlagsManager().registerFlag(SHOP_TRAPPED_CHEST_FLAG) && BentoBox.getInstance().getFlagsManager().registerFlag(SHOP_SHULKER_BOX_FLAG) && BentoBox.getInstance().getFlagsManager().registerFlag(SHOP_BARREL_FLAG)) { + plugin.debug("Registered BentoBox shop flags"); } else { - plugin.getLogger().warning("Failed to register BentoBox shop flag"); - plugin.debug("Failed to register BentoBox shop flag"); + plugin.getLogger().warning("Failed to register BentoBox shop flags"); + plugin.debug("Failed to register BentoBox shop flags"); } } } \ No newline at end of file diff --git a/plugin/src/main/java/de/epiceric/shopchest/external/listeners/BentoBoxListener.java b/plugin/src/main/java/de/epiceric/shopchest/external/listeners/BentoBoxListener.java index c638d58..736bba9 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/external/listeners/BentoBoxListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/external/listeners/BentoBoxListener.java @@ -45,8 +45,12 @@ public class BentoBoxListener extends FlagListener { } private boolean handleForLocation(Player player, Location loc, Cancellable e) { - boolean allowed = checkIsland((Event) e, player, loc, BentoBoxShopFlag.SHOP_FLAG); - if (!allowed) { + boolean chest_allowed = checkIsland((Event) e, player, loc, BentoBoxShopFlag.SHOP_CHEST_FLAG); + boolean barrel_allowed = checkIsland((Event) e, player, loc, BentoBoxShopFlag.SHOP_BARREL_FLAG); + boolean shulker_allowed = checkIsland((Event) e, player, loc, BentoBoxShopFlag.SHOP_SHULKER_BOX_FLAG); + boolean trapped_chest_allowed = checkIsland((Event) e, player, loc, BentoBoxShopFlag.SHOP_TRAPPED_CHEST_FLAG); + + if (!chest_allowed || !barrel_allowed || !shulker_allowed || !trapped_chest_allowed) { e.setCancelled(true); plugin.debug("Cancel Reason: BentoBox"); return true; diff --git a/plugin/src/main/java/de/epiceric/shopchest/external/listeners/PlotSquaredListener.java b/plugin/src/main/java/de/epiceric/shopchest/external/listeners/PlotSquaredListener.java index c102de3..2b000b0 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/external/listeners/PlotSquaredListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/external/listeners/PlotSquaredListener.java @@ -1,24 +1,29 @@ package de.epiceric.shopchest.external.listeners; -import java.util.Set; - +import com.google.common.eventbus.Subscribe; +import com.plotsquared.core.events.PlotClearEvent; +import com.plotsquared.core.events.PlotDeleteEvent; import com.plotsquared.core.location.Location; import com.plotsquared.core.plot.Plot; - -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; - +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.event.ShopCreateEvent; import de.epiceric.shopchest.event.ShopExtendEvent; import de.epiceric.shopchest.external.PlotSquaredOldShopFlag; import de.epiceric.shopchest.external.PlotSquaredShopFlag; +import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.utils.Utils; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import java.util.Set; + +@SuppressWarnings("UnstableApiUsage") public class PlotSquaredListener implements Listener { private final ShopChest plugin; @@ -46,6 +51,30 @@ public class PlotSquaredListener implements Listener { handleForLocation(e.getPlayer(), e.getNewChestLocation(), e); } + @Subscribe + public void onDeletePlot(PlotDeleteEvent event) { + removeShopsFromPlot(event.getPlot()); + } + + @Subscribe + public void onClearPlot(PlotClearEvent event) { + removeShopsFromPlot(event.getPlot()); + } + + private void removeShopsFromPlot(Plot plot) { + Set regions = plot.getRegions(); + for (Shop shop: plugin.getShopUtils().getShops()) { + for (CuboidRegion region: regions) { + org.bukkit.Location loc = shop.getLocation(); + if (!region.contains(BlockVector3.at(loc.getX(), loc.getY(), loc.getZ()))) { + continue; + } + + plugin.getShopUtils().removeShop(shop, true); + } + } + } + // TODO: Outsource shop use external permission // @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @@ -87,4 +116,4 @@ public class PlotSquaredListener implements Listener { } return false; } -} \ No newline at end of file +} diff --git a/plugin/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java b/plugin/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java index 44d7afa..a193327 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java +++ b/plugin/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java @@ -2588,9 +2588,14 @@ public class LanguageUtils { messages.add(new LocalizedMessage(Message.COMMAND_DESC_LIMITS, langConfig.getString("message.commandDescription.limits", "&a/%COMMAND% limits - View shop limits."))); messages.add(new LocalizedMessage(Message.COMMAND_DESC_OPEN, langConfig.getString("message.commandDescription.open", "&a/%COMMAND% open - Open a shop."))); messages.add(new LocalizedMessage(Message.COMMAND_DESC_CONFIG, langConfig.getString("message.commandDescription.config", "&a/%COMMAND% config - Change configuration values."))); + messages.add(new LocalizedMessage(Message.COMMAND_DESC_VALUE, langConfig.getString("message.commandDescription.value", "&a/%COMMAND% value - Get the average price of an item."))); messages.add(new LocalizedMessage(Message.CHANGED_CONFIG_SET, langConfig.getString("message.config.set", "&6Changed &a%PROPERTY% &6to &a%VALUE%&6."))); messages.add(new LocalizedMessage(Message.CHANGED_CONFIG_REMOVED, langConfig.getString("message.config.removed", "&6Removed &a%VALUE% &6from &a%PROPERTY%&6."))); messages.add(new LocalizedMessage(Message.CHANGED_CONFIG_ADDED, langConfig.getString("message.config.added", "&6Added &a%VALUE% &6to &a%PROPERTY%&6."))); + messages.add(new LocalizedMessage(Message.VALUE_OF_ITEM_BUY, langConfig.getString("message.valueOfItemBuy", "&6This item can be bought at &a%AMOUNT% &6players for an average price of &a%BUY-PRICE%&6."))); + messages.add(new LocalizedMessage(Message.VALUE_OF_ITEM_SELL, langConfig.getString("message.valueOfItemSell", "&6This item can be sold at &a%AMOUNT% &6players for an average price of &a%SELL-PRICE%&6."))); + messages.add(new LocalizedMessage(Message.VALUE_NO_SHOPS_BUY, langConfig.getString("message.valueNoShopsBuy", "&cNo shops are currently letting you buy this item."))); + messages.add(new LocalizedMessage(Message.VALUE_NO_SHOPS_SELL, langConfig.getString("message.valueNoShopsSell", "&cNo shops are currently letting you sell this item."))); } /** diff --git a/plugin/src/main/java/de/epiceric/shopchest/language/Message.java b/plugin/src/main/java/de/epiceric/shopchest/language/Message.java index db168a5..ec18ccc 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/language/Message.java +++ b/plugin/src/main/java/de/epiceric/shopchest/language/Message.java @@ -93,7 +93,12 @@ public enum Message { COMMAND_DESC_LIMITS, COMMAND_DESC_OPEN, COMMAND_DESC_CONFIG, + COMMAND_DESC_VALUE, CHANGED_CONFIG_SET, CHANGED_CONFIG_REMOVED, - CHANGED_CONFIG_ADDED + CHANGED_CONFIG_ADDED, + VALUE_OF_ITEM_BUY, + VALUE_OF_ITEM_SELL, + VALUE_NO_SHOPS_BUY, + VALUE_NO_SHOPS_SELL } diff --git a/plugin/src/main/java/de/epiceric/shopchest/listeners/BlockExplodeListener.java b/plugin/src/main/java/de/epiceric/shopchest/listeners/BlockExplodeListener.java index 55e0a4f..bb38a84 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/listeners/BlockExplodeListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/listeners/BlockExplodeListener.java @@ -2,7 +2,7 @@ package de.epiceric.shopchest.listeners; import java.util.ArrayList; -import org.bukkit.Material; +import de.epiceric.shopchest.utils.ShopUtils; import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -22,7 +22,7 @@ public class BlockExplodeListener implements Listener { public void onBlockExplode(BlockExplodeEvent e) { ArrayList bl = new ArrayList<>(e.blockList()); for (Block b : bl) { - if (b.getType().equals(Material.CHEST) || b.getType().equals(Material.TRAPPED_CHEST)) { + if (ShopUtils.isShopMaterial(b.getType())) { if (plugin.getShopUtils().isShop(b.getLocation())) e.blockList().remove(b); } } diff --git a/plugin/src/main/java/de/epiceric/shopchest/listeners/ChestProtectListener.java b/plugin/src/main/java/de/epiceric/shopchest/listeners/ChestProtectListener.java index b7af654..ec63f9c 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/listeners/ChestProtectListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/listeners/ChestProtectListener.java @@ -1,20 +1,19 @@ package de.epiceric.shopchest.listeners; import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Chest; -import org.bukkit.block.DoubleChest; +import org.bukkit.block.*; import org.bukkit.block.data.type.Chest.Type; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent; @@ -64,22 +63,24 @@ public class ChestProtectListener implements Listener { } }); } else { - double creationPrice = shop.getShopType() == ShopType.ADMIN ? Config.shopCreationPriceAdmin : Config.shopCreationPriceNormal; - if (creationPrice > 0 && Config.refundShopCreation && p.getUniqueId().equals(shop.getVendor().getUniqueId())) { - EconomyResponse r = plugin.getEconomy().depositPlayer(p, shop.getLocation().getWorld().getName(), creationPrice); - if (!r.transactionSuccess()) { - plugin.debug("Economy transaction failed: " + r.errorMessage); - p.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, - new Replacement(Placeholder.ERROR, r.errorMessage))); - p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, - new Replacement(Placeholder.CREATION_PRICE, 0))); + CompletableFuture.runAsync(() -> { + double creationPrice = shop.getShopType() == ShopType.ADMIN ? Config.shopCreationPriceAdmin : Config.shopCreationPriceNormal; + if (creationPrice > 0 && Config.refundShopCreation && p.getUniqueId().equals(shop.getVendor().getUniqueId())) { + EconomyResponse r = plugin.getEconomy().depositPlayer(p, shop.getLocation().getWorld().getName(), creationPrice); + if (!r.transactionSuccess()) { + plugin.debug("Economy transaction failed: " + r.errorMessage); + p.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, + new Replacement(Placeholder.ERROR, r.errorMessage))); + p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, + new Replacement(Placeholder.CREATION_PRICE, 0))); + } else { + p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, + new Replacement(Placeholder.CREATION_PRICE, creationPrice))); + } } else { - p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, - new Replacement(Placeholder.CREATION_PRICE, creationPrice))); + p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED)); } - } else { - p.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED)); - } + }); shopUtils.removeShop(shop, true); plugin.debug(String.format("%s broke %s's shop (#%d)", p.getName(), shop.getVendor().getName(), shop.getID())); @@ -123,22 +124,44 @@ public class ChestProtectListener implements Listener { public void onEntityExplode(EntityExplodeEvent e) { ArrayList bl = new ArrayList<>(e.blockList()); for (Block b : bl) { - if (b.getType().equals(Material.CHEST) || b.getType().equals(Material.TRAPPED_CHEST)) { + if (ShopUtils.isShopMaterial(b.getType())) { if (shopUtils.isShop(b.getLocation())) e.blockList().remove(b); } } } + @EventHandler(ignoreCancelled = true) + public void onPistonExtend(BlockPistonExtendEvent e) { + for (Block b: e.getBlocks()) { + if (ShopUtils.isShopMaterial(b.getType())) { + if (shopUtils.isShop(b.getLocation())) { + e.setCancelled(true); + } + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onPistonRetract(BlockPistonRetractEvent e) { + for (Block b: e.getBlocks()) { + if (ShopUtils.isShopMaterial(b.getType())) { + if (shopUtils.isShop(b.getLocation())) { + e.setCancelled(true); + } + } + } + } + @EventHandler(ignoreCancelled = true) public void onBlockPlace(BlockPlaceEvent e) { final Player p = e.getPlayer(); final Block b = e.getBlockPlaced(); - if (!b.getType().equals(Material.CHEST) && !b.getType().equals(Material.TRAPPED_CHEST)) { + if (!ShopUtils.isShopMaterial(b.getType())) { return; } - Chest c = (Chest) b.getState(); + Container c = (Container) b.getState(); Block b2; // Can't use Utils::getChestLocations since inventory holder @@ -160,9 +183,13 @@ public class ChestProtectListener implements Listener { b2 = l.getBlock(); } } else { + if (!(c instanceof Chest)) { + return; + } + org.bukkit.block.data.type.Chest data = (org.bukkit.block.data.type.Chest) c.getBlockData(); - if (data.getType() == Type.SINGLE) { + if (data.equals(Type.SINGLE)) { return; } @@ -228,7 +255,7 @@ public class ChestProtectListener implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onItemMove(InventoryMoveItemEvent e) { - if ((e.getSource().getType().equals(InventoryType.CHEST)) && (!e.getInitiator().getType().equals(InventoryType.PLAYER))) { + if ((e.getSource().getType().equals(InventoryType.CHEST) || e.getSource().getType().equals(InventoryType.SHULKER_BOX) || e.getSource().getType().equals(InventoryType.BARREL)) && (!e.getInitiator().getType().equals(InventoryType.PLAYER))) { if (e.getSource().getHolder() instanceof DoubleChest) { DoubleChest dc = (DoubleChest) e.getSource().getHolder(); @@ -237,8 +264,8 @@ public class ChestProtectListener implements Listener { if (shopUtils.isShop(r.getLocation()) || shopUtils.isShop(l.getLocation())) e.setCancelled(true); - } else if (e.getSource().getHolder() instanceof Chest) { - Chest c = (Chest) e.getSource().getHolder(); + } else if (e.getSource().getHolder() instanceof Chest || e.getSource().getHolder() instanceof ShulkerBox || e.getSource().getHolder() instanceof Barrel) { + Container c = (Container) e.getSource().getHolder(); if (shopUtils.isShop(c.getLocation())) e.setCancelled(true); } diff --git a/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index 289cc32..ea2e49c 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -3,7 +3,11 @@ package de.epiceric.shopchest.listeners; import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.config.Placeholder; -import de.epiceric.shopchest.event.*; +import de.epiceric.shopchest.event.ShopBuySellEvent; +import de.epiceric.shopchest.event.ShopCreateEvent; +import de.epiceric.shopchest.event.ShopInfoEvent; +import de.epiceric.shopchest.event.ShopOpenEvent; +import de.epiceric.shopchest.event.ShopRemoveEvent; import de.epiceric.shopchest.external.PlotSquaredOldShopFlag; import de.epiceric.shopchest.external.PlotSquaredShopFlag; import de.epiceric.shopchest.language.LanguageUtils; @@ -13,8 +17,12 @@ import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.shop.ShopProduct; import de.epiceric.shopchest.sql.Database; -import de.epiceric.shopchest.utils.*; +import de.epiceric.shopchest.utils.ClickType; import de.epiceric.shopchest.utils.ClickType.CreateClickType; +import de.epiceric.shopchest.utils.ItemUtils; +import de.epiceric.shopchest.utils.Permissions; +import de.epiceric.shopchest.utils.ShopUtils; +import de.epiceric.shopchest.utils.Utils; import fr.xephi.authme.api.v3.AuthMeApi; import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.EconomyResponse; @@ -22,11 +30,16 @@ import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.block.Barrel; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; import org.bukkit.block.Chest; +import org.bukkit.block.Container; import org.bukkit.block.DoubleChest; +import org.bukkit.block.ShulkerBox; import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -44,9 +57,14 @@ import org.codemc.worldguardwrapper.flag.WrappedState; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.regex.Pattern; public class ShopInteractListener implements Listener { @@ -68,17 +86,18 @@ public class ShopInteractListener implements Listener { Inventory chestInv = e.getInventory(); - if (!(chestInv.getHolder() instanceof Chest || chestInv.getHolder() instanceof DoubleChest)) { + if (!(chestInv.getHolder() instanceof Chest || chestInv.getHolder() instanceof ShulkerBox || chestInv.getHolder() instanceof Barrel || chestInv.getHolder() instanceof DoubleChest)) { return; } Location loc = null; - if (chestInv.getHolder() instanceof Chest) { - loc = ((Chest) chestInv.getHolder()).getLocation(); + if (chestInv.getHolder() instanceof Chest || chestInv.getHolder() instanceof ShulkerBox || chestInv.getHolder() instanceof Barrel) { + loc = ((BlockState) chestInv.getHolder()).getLocation(); } else if (chestInv.getHolder() instanceof DoubleChest) { loc = ((DoubleChest) chestInv.getHolder()).getLocation(); } + if (loc == null) return; final Shop shop = plugin.getShopUtils().getShop(loc); if (shop == null) return; @@ -101,7 +120,7 @@ public class ShopInteractListener implements Listener { if (!(ClickType.getPlayerClickType(p) instanceof CreateClickType)) return; - if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) + if (!ShopUtils.isShopMaterial(b.getType())) return; if (ClickType.getPlayerClickType(p).getClickType() != ClickType.EnumClickType.CREATE) @@ -110,7 +129,7 @@ public class ShopInteractListener implements Listener { if (Config.enableAuthMeIntegration && plugin.hasAuthMe() && !AuthMeApi.getInstance().isAuthenticated(p)) return; - if (e.isCancelled() && !p.hasPermission(Permissions.CREATE_PROTECTED)) { + if (e.useInteractedBlock() == Event.Result.DENY && !p.hasPermission(Permissions.CREATE_PROTECTED)) { p.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_CREATE_PROTECTED)); plugin.debug(p.getName() + " is not allowed to create a shop on the selected chest"); } else if (shopUtils.isShop(b.getLocation())) { @@ -145,8 +164,8 @@ public class ShopInteractListener implements Listener { if (e.getAction() != Action.RIGHT_CLICK_BLOCK && e.getAction() != Action.LEFT_CLICK_BLOCK) return; - - if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) + + if (b == null || !ShopUtils.isShopMaterial(b.getType())) return; ClickType clickType = ClickType.getPlayerClickType(p); @@ -283,7 +302,7 @@ public class ShopInteractListener implements Listener { } } else { if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) { - Chest c = (Chest) b.getState(); + Container c = (Container) b.getState(); ItemStack itemStack = shop.getProduct().getItemStack(); int amount = (p.isSneaking() ? itemStack.getMaxStackSize() : shop.getProduct().getAmount()); @@ -482,22 +501,28 @@ public class ShopInteractListener implements Listener { return; } - if (creationPrice > 0) { - EconomyResponse r = plugin.getEconomy().withdrawPlayer(executor, location.getWorld().getName(), creationPrice); - if (!r.transactionSuccess()) { - plugin.debug("Economy transaction failed: " + r.errorMessage); - executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r.errorMessage))); - return; + CompletableFuture.runAsync(() -> { + + if (creationPrice > 0) { + EconomyResponse r = econ.withdrawPlayer(executor, location.getWorld().getName(), creationPrice); + if (!r.transactionSuccess()) { + plugin.debug("Economy transaction failed: " + r.errorMessage); + executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r.errorMessage))); + return; + } } - } - shop.create(true); + Bukkit.getScheduler().runTask(plugin, () -> { - plugin.debug("Shop created"); - shopUtils.addShop(shop, true); + shop.create(true); - Message message = shopType == ShopType.ADMIN ? Message.ADMIN_SHOP_CREATED : Message.SHOP_CREATED; - executor.sendMessage(LanguageUtils.getMessage(message, new Replacement(Placeholder.CREATION_PRICE, creationPrice))); + plugin.debug("Shop created"); + shopUtils.addShop(shop, true); + + Message message = shopType == ShopType.ADMIN ? Message.ADMIN_SHOP_CREATED : Message.SHOP_CREATED; + executor.sendMessage(LanguageUtils.getMessage(message, new Replacement(Placeholder.CREATION_PRICE, creationPrice))); + }); + }, plugin.getShopCreationThreadPool()); } /** @@ -525,25 +550,31 @@ public class ShopInteractListener implements Listener { return; } - double creationPrice = shop.getShopType() == ShopType.ADMIN ? Config.shopCreationPriceAdmin : Config.shopCreationPriceNormal; - if (creationPrice > 0 && Config.refundShopCreation && executor.getUniqueId().equals(shop.getVendor().getUniqueId())) { - EconomyResponse r = plugin.getEconomy().depositPlayer(executor, shop.getLocation().getWorld().getName(), creationPrice); - if (!r.transactionSuccess()) { - plugin.debug("Economy transaction failed: " + r.errorMessage); - executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, - new Replacement(Placeholder.ERROR, r.errorMessage))); - executor.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, - new Replacement(Placeholder.CREATION_PRICE, 0))); - } else { - executor.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, - new Replacement(Placeholder.CREATION_PRICE, creationPrice))); - } - } else { - executor.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED)); - } + CompletableFuture.runAsync(() -> { - shopUtils.removeShop(shop, true); - plugin.debug("Removed shop (#" + shop.getID() + ")"); + double creationPrice = shop.getShopType() == ShopType.ADMIN ? Config.shopCreationPriceAdmin : Config.shopCreationPriceNormal; + if (creationPrice > 0 && Config.refundShopCreation && executor.getUniqueId().equals(shop.getVendor().getUniqueId())) { + EconomyResponse r = econ.depositPlayer(executor, shop.getLocation().getWorld().getName(), creationPrice); + if (!r.transactionSuccess()) { + plugin.debug("Economy transaction failed: " + r.errorMessage); + executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, + new Replacement(Placeholder.ERROR, r.errorMessage))); + executor.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, + new Replacement(Placeholder.CREATION_PRICE, 0))); + } else { + executor.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED_REFUND, + new Replacement(Placeholder.CREATION_PRICE, creationPrice))); + } + } else { + executor.sendMessage(LanguageUtils.getMessage(Message.SHOP_REMOVED)); + } + + Bukkit.getScheduler().runTask(plugin, () -> { + + shopUtils.removeShop(shop, true); + plugin.debug("Removed shop (#" + shop.getID() + ")"); + }); + }, plugin.getShopCreationThreadPool()); } /** @@ -587,7 +618,7 @@ public class ShopInteractListener implements Listener { return; } - Chest c = (Chest) shop.getLocation().getBlock().getState(); + Container c = (Container) shop.getLocation().getBlock().getState(); ItemStack itemStack = shop.getProduct().getItemStack(); int amount = Utils.getAmount(c.getInventory(), itemStack); int space = Utils.getFreeSpaceForItem(c.getInventory(), itemStack); @@ -648,155 +679,180 @@ public class ShopInteractListener implements Listener { String worldName = shop.getLocation().getWorld().getName(); - double price = shop.getBuyPrice(); - if (stack) price = (price / shop.getProduct().getAmount()) * amount; + int finalAmount = amount; + CompletableFuture.runAsync(() -> { - if (econ.getBalance(executor, worldName) >= price || Config.autoCalculateItemAmount) { + double price = shop.getBuyPrice(); + if (stack) price = (price / shop.getProduct().getAmount()) * finalAmount; - int amountForMoney = (int) (amount / price * econ.getBalance(executor, worldName)); + if (econ.getBalance(executor, worldName) >= price || Config.autoCalculateItemAmount) { - if (amountForMoney == 0 && Config.autoCalculateItemAmount) { - executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_MONEY)); - return; - } + int amountForMoney = (int) (finalAmount / price * econ.getBalance(executor, worldName)); - plugin.debug(executor.getName() + " has enough money for " + amountForMoney + " item(s) (#" + shop.getID() + ")"); - - Block b = shop.getLocation().getBlock(); - Chest c = (Chest) b.getState(); - - int amountForChestItems = Utils.getAmount(c.getInventory(), itemStack); - - if (amountForChestItems == 0 && shop.getShopType() != ShopType.ADMIN) { - executor.sendMessage(LanguageUtils.getMessage(Message.OUT_OF_STOCK)); - return; - } - - ItemStack product = new ItemStack(itemStack); - if (stack) product.setAmount(amount); - - Inventory inventory = executor.getInventory(); - - int freeSpace = Utils.getFreeSpaceForItem(inventory, product); - - if (freeSpace == 0) { - executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_INVENTORY_SPACE)); - return; - } - - int newAmount = amount; - - if (Config.autoCalculateItemAmount) { - if (shop.getShopType() == ShopType.ADMIN) - newAmount = Math.min(amountForMoney, freeSpace); - else - newAmount = Math.min(Math.min(amountForMoney, amountForChestItems), freeSpace); - } - - if (newAmount > amount) newAmount = amount; - - ShopProduct newProduct = new ShopProduct(product, newAmount); - double newPrice = (price / amount) * newAmount; - - if (freeSpace >= newAmount) { - plugin.debug(executor.getName() + " has enough inventory space for " + freeSpace + " items (#" + shop.getID() + ")"); - - EconomyResponse r = econ.withdrawPlayer(executor, worldName, newPrice); - - if (r.transactionSuccess()) { - EconomyResponse r2 = (shop.getShopType() != ShopType.ADMIN) ? econ.depositPlayer(shop.getVendor(), worldName, newPrice) : null; - - if (r2 != null) { - if (r2.transactionSuccess()) { - ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.BUY, newAmount, newPrice); - Bukkit.getPluginManager().callEvent(event); - - if (event.isCancelled()) { - econ.depositPlayer(executor, worldName, newPrice); - econ.withdrawPlayer(shop.getVendor(), worldName, newPrice); - plugin.debug("Buy event cancelled (#" + shop.getID() + ")"); - return; - } - - database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.BUY, null); - - addToInventory(inventory, newProduct); - removeFromInventory(c.getInventory(), newProduct); - executor.updateInventory(); - - new BukkitRunnable() { - @Override - public void run() { - if (plugin.getHologramFormat().isDynamic()) { - shop.updateHologramText(); - } - } - }.runTaskLater(plugin, 1L); - - String vendorName = (shop.getVendor().getName() == null ? shop.getVendor().getUniqueId().toString() : shop.getVendor().getName()); - executor.sendMessage(LanguageUtils.getMessage(Message.BUY_SUCCESS, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)), - new Replacement(Placeholder.VENDOR, vendorName))); - - plugin.debug(executor.getName() + " successfully bought (#" + shop.getID() + ")"); - - if (shop.getVendor().isOnline() && Config.enableVendorMessages) { - shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.SOMEONE_BOUGHT, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)), - new Replacement(Placeholder.PLAYER, executor.getName()))); - } else if(!shop.getVendor().isOnline() && Config.enableVendorBungeeMessages){ - String message = LanguageUtils.getMessage( Message.SOMEONE_BOUGHT, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)), - new Replacement(Placeholder.PLAYER, executor.getName())); - sendBungeeMessage(shop.getVendor().getName(),message); - } - - } else { - plugin.debug("Economy transaction failed (r2): " + r2.errorMessage + " (#" + shop.getID() + ")"); - executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r2.errorMessage))); - econ.withdrawPlayer(shop.getVendor(), worldName, newPrice); - econ.depositPlayer(executor, worldName, newPrice); - } - } else { - ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.BUY, newAmount, newPrice); - Bukkit.getPluginManager().callEvent(event); - - if (event.isCancelled()) { - econ.depositPlayer(executor, worldName, newPrice); - plugin.debug("Buy event cancelled (#" + shop.getID() + ")"); - return; - } - - database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.BUY, null); - - addToInventory(inventory, newProduct); - executor.updateInventory(); - - new BukkitRunnable() { - @Override - public void run() { - if (plugin.getHologramFormat().isDynamic()) { - shop.updateHologramText(); - } - } - }.runTaskLater(plugin, 1L); - - executor.sendMessage(LanguageUtils.getMessage(Message.BUY_SUCCESS_ADMIN, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)))); - - plugin.debug(executor.getName() + " successfully bought (#" + shop.getID() + ")"); - } - } else { - plugin.debug("Economy transaction failed (r): " + r.errorMessage + " (#" + shop.getID() + ")"); - executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r.errorMessage))); - econ.depositPlayer(executor, worldName, newPrice); + if (amountForMoney == 0 && Config.autoCalculateItemAmount) { + executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_MONEY)); + return; } + + plugin.debug(executor.getName() + " has enough money for " + amountForMoney + " item(s) (#" + shop.getID() + ")"); + + double finalPrice = price; + Bukkit.getScheduler().runTask(plugin, () -> { + + Block b = shop.getLocation().getBlock(); + Container c = (Container) b.getState(); + + int amountForChestItems = Utils.getAmount(c.getInventory(), itemStack); + + if (amountForChestItems == 0 && shop.getShopType() != ShopType.ADMIN) { + executor.sendMessage(LanguageUtils.getMessage(Message.OUT_OF_STOCK)); + return; + } + + ItemStack product = new ItemStack(itemStack); + if (stack) product.setAmount(finalAmount); + + Inventory inventory = executor.getInventory(); + + int freeSpace = Utils.getFreeSpaceForItem(inventory, product); + + if (freeSpace == 0) { + executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_INVENTORY_SPACE)); + return; + } + + CompletableFuture.runAsync(() -> { + + int newAmount = finalAmount; + + if (Config.autoCalculateItemAmount) { + if (shop.getShopType() == ShopType.ADMIN) + newAmount = Math.min(amountForMoney, freeSpace); + else + newAmount = Math.min(Math.min(amountForMoney, amountForChestItems), freeSpace); + } + + if (newAmount > finalAmount) newAmount = finalAmount; + + ShopProduct newProduct = new ShopProduct(product, newAmount); + double newPrice = (finalPrice / finalAmount) * newAmount; + double tax = Config.shopTaxes.getOrDefault(itemStack.getType().toString(), Config.shopTaxes.get("default")); + + if (freeSpace >= newAmount) { + plugin.debug(executor.getName() + " has enough inventory space for " + freeSpace + " items (#" + shop.getID() + ")"); + + EconomyResponse r = econ.withdrawPlayer(executor, worldName, newPrice); + + if (r.transactionSuccess()) { + EconomyResponse r2 = (shop.getShopType() != ShopType.ADMIN) ? econ.depositPlayer(shop.getVendor(), worldName, newPrice * (100d - tax) / 100d) : null; + + if (r2 != null) { + if (r2.transactionSuccess()) { + + int finalNewAmount = newAmount; + Bukkit.getScheduler().runTask(plugin, () -> { + ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.BUY, finalNewAmount, newPrice); + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + econ.depositPlayer(executor, worldName, newPrice); + econ.withdrawPlayer(shop.getVendor(), worldName, newPrice * (100d - tax) / 100d); + plugin.debug("Buy event cancelled (#" + shop.getID() + ")"); + return; + } + + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.BUY, null); + + addToInventory(inventory, newProduct); + removeFromInventory(c.getInventory(), newProduct); + executor.updateInventory(); + + new BukkitRunnable() { + @Override + public void run() { + if (plugin.getHologramFormat().isDynamic()) { + shop.updateHologramText(); + } + } + }.runTaskLater(plugin, 1L); + + String vendorName = (shop.getVendor().getName() == null ? shop.getVendor().getUniqueId().toString() : shop.getVendor().getName()); + executor.sendMessage(LanguageUtils.getMessage(Message.BUY_SUCCESS, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)), + new Replacement(Placeholder.VENDOR, vendorName))); + + plugin.debug(executor.getName() + " successfully bought (#" + shop.getID() + ")"); + plugin.getLogger().info(String.format("%s bought %d of %s from %s", executor.getName(), finalNewAmount, newProduct.getItemStack().toString(), vendorName)); + + if (shop.getVendor().isOnline() && Config.enableVendorMessages) { + shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.SOMEONE_BOUGHT, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)), + new Replacement(Placeholder.PLAYER, executor.getName()))); + } else if (!shop.getVendor().isOnline() && Config.enableVendorBungeeMessages) { + String message = LanguageUtils.getMessage(Message.SOMEONE_BOUGHT, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)), + new Replacement(Placeholder.PLAYER, executor.getName())); + sendBungeeMessage(shop.getVendor().getName(), message); + } + }); + + } else { + CompletableFuture.runAsync(() -> { + plugin.debug("Economy transaction failed (r2): " + r2.errorMessage + " (#" + shop.getID() + ")"); + executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r2.errorMessage))); + econ.withdrawPlayer(shop.getVendor(), worldName, newPrice); + econ.depositPlayer(executor, worldName, newPrice * (100d - tax) / 100d); + }, plugin.getShopCreationThreadPool()); + } + } else { + int finalNewAmount1 = newAmount; + Bukkit.getScheduler().runTask(plugin, () -> { + ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.BUY, finalNewAmount1, newPrice); + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + econ.depositPlayer(executor, worldName, newPrice * (100d - tax) / 100d); + plugin.debug("Buy event cancelled (#" + shop.getID() + ")"); + return; + } + + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.BUY, null); + + addToInventory(inventory, newProduct); + executor.updateInventory(); + + new BukkitRunnable() { + @Override + public void run() { + if (plugin.getHologramFormat().isDynamic()) { + shop.updateHologramText(); + } + } + }.runTaskLater(plugin, 1L); + + executor.sendMessage(LanguageUtils.getMessage(Message.BUY_SUCCESS_ADMIN, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount1)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.BUY_PRICE, String.valueOf(newPrice)))); + + plugin.debug(executor.getName() + " successfully bought (#" + shop.getID() + ")"); + plugin.getLogger().info(String.format("%s bought %d of %s from %s", executor.getName(), finalNewAmount1, newProduct.getItemStack().toString(), "ADMIN")); + }); + } + } else { + CompletableFuture.runAsync(() -> { + plugin.debug("Economy transaction failed (r): " + r.errorMessage + " (#" + shop.getID() + ")"); + executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r.errorMessage))); + econ.depositPlayer(executor, worldName, newPrice); + }, plugin.getShopCreationThreadPool()); + } + } else { + executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_INVENTORY_SPACE)); + } + }, plugin.getShopCreationThreadPool()); + }); } else { - executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_INVENTORY_SPACE)); + executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_MONEY)); } - } else { - executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_MONEY)); - } + }, plugin.getShopCreationThreadPool()); } /** @@ -816,165 +872,189 @@ public class ShopInteractListener implements Listener { String worldName = shop.getLocation().getWorld().getName(); - if (shop.getShopType() == ShopType.ADMIN || econ.getBalance(shop.getVendor(), worldName) >= price || Config.autoCalculateItemAmount) { - int amountForMoney = 1; - - if (shop.getShopType() != ShopType.ADMIN) { - amountForMoney = (int) (amount / price * econ.getBalance(shop.getVendor(), worldName)); - } + int finalAmount = amount; + double finalPrice = price; + CompletableFuture.runAsync(() -> { - plugin.debug("Vendor has enough money for " + amountForMoney + " item(s) (#" + shop.getID() + ")"); + if (shop.getShopType() == ShopType.ADMIN || econ.getBalance(shop.getVendor(), worldName) >= finalPrice || Config.autoCalculateItemAmount) { + int amountForMoney = 1; - if (amountForMoney == 0 && Config.autoCalculateItemAmount && shop.getShopType() != ShopType.ADMIN) { - executor.sendMessage(LanguageUtils.getMessage(Message.VENDOR_NOT_ENOUGH_MONEY)); - return; - } - - Block block = shop.getLocation().getBlock(); - Chest chest = (Chest) block.getState(); - - int amountForItemCount = Utils.getAmount(executor.getInventory(), itemStack); - - if (amountForItemCount == 0) { - executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_ITEMS)); - return; - } - - ItemStack product = new ItemStack(itemStack); - if (stack) product.setAmount(amount); - - Inventory inventory = chest.getInventory(); - - int freeSpace = Utils.getFreeSpaceForItem(inventory, product); - - if (freeSpace == 0 && shop.getShopType() != ShopType.ADMIN) { - executor.sendMessage(LanguageUtils.getMessage(Message.CHEST_NOT_ENOUGH_INVENTORY_SPACE)); - return; - } - - int newAmount = amount; - - if (Config.autoCalculateItemAmount) { - if (shop.getShopType() == ShopType.ADMIN) - newAmount = amountForItemCount; - else - newAmount = Math.min(Math.min(amountForMoney, amountForItemCount), freeSpace); - } - - if (newAmount > amount) newAmount = amount; - - ShopProduct newProduct = new ShopProduct(product, newAmount); - double newPrice = (price / amount) * newAmount; - - if (freeSpace >= newAmount || shop.getShopType() == ShopType.ADMIN) { - plugin.debug("Chest has enough inventory space for " + freeSpace + " items (#" + shop.getID() + ")"); - - EconomyResponse r = econ.depositPlayer(executor, worldName, newPrice); - - if (r.transactionSuccess()) { - EconomyResponse r2 = (shop.getShopType() != ShopType.ADMIN) ? econ.withdrawPlayer(shop.getVendor(), worldName, newPrice) : null; - - if (r2 != null) { - if (r2.transactionSuccess()) { - ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.SELL, newAmount, newPrice); - Bukkit.getPluginManager().callEvent(event); - - if (event.isCancelled()) { - econ.withdrawPlayer(executor, worldName, newPrice); - econ.depositPlayer(shop.getVendor(), worldName, newPrice); - plugin.debug("Sell event cancelled (#" + shop.getID() + ")"); - return; - } - - database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.SELL, null); - - addToInventory(inventory, newProduct); - removeFromInventory(executor.getInventory(), newProduct); - executor.updateInventory(); - - new BukkitRunnable() { - @Override - public void run() { - if (plugin.getHologramFormat().isDynamic()) { - shop.updateHologramText(); - } - } - }.runTaskLater(plugin, 1L); - - String vendorName = (shop.getVendor().getName() == null ? shop.getVendor().getUniqueId().toString() : shop.getVendor().getName()); - executor.sendMessage(LanguageUtils.getMessage(Message.SELL_SUCCESS, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)), - new Replacement(Placeholder.VENDOR, vendorName))); - - plugin.debug(executor.getName() + " successfully sold (#" + shop.getID() + ")"); - - if (shop.getVendor().isOnline() && Config.enableVendorMessages) { - shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.SOMEONE_SOLD, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)), - new Replacement(Placeholder.PLAYER, executor.getName()))); - } else if(!shop.getVendor().isOnline() && Config.enableVendorBungeeMessages){ - String message = LanguageUtils.getMessage( Message.SOMEONE_SOLD, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)), - new Replacement(Placeholder.PLAYER, executor.getName())); - sendBungeeMessage(shop.getVendor().getName(),message); - } - - } else { - plugin.debug("Economy transaction failed (r2): " + r2.errorMessage + " (#" + shop.getID() + ")"); - executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r2.errorMessage))); - econ.withdrawPlayer(executor, worldName, newPrice); - econ.depositPlayer(shop.getVendor(), worldName, newPrice); - } - - } else { - ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.SELL, newAmount, newPrice); - Bukkit.getPluginManager().callEvent(event); - - if (event.isCancelled()) { - econ.withdrawPlayer(executor, worldName, newPrice); - plugin.debug("Sell event cancelled (#" + shop.getID() + ")"); - return; - } - - database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.SELL, null); - - removeFromInventory(executor.getInventory(), newProduct); - executor.updateInventory(); - - new BukkitRunnable() { - @Override - public void run() { - if (plugin.getHologramFormat().isDynamic()) { - shop.updateHologramText(); - } - } - }.runTaskLater(plugin, 1L); - - executor.sendMessage(LanguageUtils.getMessage(Message.SELL_SUCCESS_ADMIN, new Replacement(Placeholder.AMOUNT, String.valueOf(newAmount)), - new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)))); - - plugin.debug(executor.getName() + " successfully sold (#" + shop.getID() + ")"); - } - - } else { - plugin.debug("Economy transaction failed (r): " + r.errorMessage + " (#" + shop.getID() + ")"); - executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r.errorMessage))); - econ.withdrawPlayer(executor, worldName, newPrice); + if (shop.getShopType() != ShopType.ADMIN) { + amountForMoney = (int) (finalAmount / finalPrice * econ.getBalance(shop.getVendor(), worldName)); } - } else { - executor.sendMessage(LanguageUtils.getMessage(Message.CHEST_NOT_ENOUGH_INVENTORY_SPACE)); - } + plugin.debug("Vendor has enough money for " + amountForMoney + " item(s) (#" + shop.getID() + ")"); - } else { - executor.sendMessage(LanguageUtils.getMessage(Message.VENDOR_NOT_ENOUGH_MONEY)); - } + if (amountForMoney == 0 && Config.autoCalculateItemAmount && shop.getShopType() != ShopType.ADMIN) { + executor.sendMessage(LanguageUtils.getMessage(Message.VENDOR_NOT_ENOUGH_MONEY)); + return; + } + + int finalAmountForMoney = amountForMoney; + Bukkit.getScheduler().runTask(plugin, () -> { + Block block = shop.getLocation().getBlock(); + Container chest = (Container) block.getState(); + + int amountForItemCount = Utils.getAmount(executor.getInventory(), itemStack); + + if (amountForItemCount == 0) { + executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_ITEMS)); + return; + } + + ItemStack product = new ItemStack(itemStack); + if (stack) product.setAmount(finalAmount); + + Inventory inventory = chest.getInventory(); + + int freeSpace = Utils.getFreeSpaceForItem(inventory, product); + + if (freeSpace == 0 && shop.getShopType() != ShopType.ADMIN) { + executor.sendMessage(LanguageUtils.getMessage(Message.CHEST_NOT_ENOUGH_INVENTORY_SPACE)); + return; + } + + int newAmount = finalAmount; + + if (Config.autoCalculateItemAmount) { + if (shop.getShopType() == ShopType.ADMIN) + newAmount = amountForItemCount; + else + newAmount = Math.min(Math.min(finalAmountForMoney, amountForItemCount), freeSpace); + } + + if (newAmount > finalAmount) newAmount = finalAmount; + + ShopProduct newProduct = new ShopProduct(product, newAmount); + double newPrice = (finalPrice / finalAmount) * newAmount; + double tax = plugin.getShopChestConfig().shopTaxes.getOrDefault(itemStack.getType().toString(), plugin.getShopChestConfig().shopTaxes.get("default")); + + if (freeSpace >= newAmount || shop.getShopType() == ShopType.ADMIN) { + plugin.debug("Chest has enough inventory space for " + freeSpace + " items (#" + shop.getID() + ")"); + + int finalNewAmount = newAmount; + CompletableFuture.runAsync(() -> { + + EconomyResponse r = econ.depositPlayer(executor, worldName, newPrice * (100d - tax) / 100d); + + if (r.transactionSuccess()) { + EconomyResponse r2 = (shop.getShopType() != ShopType.ADMIN) ? econ.withdrawPlayer(shop.getVendor(), worldName, newPrice) : null; + + if (r2 != null) { + if (r2.transactionSuccess()) { + Bukkit.getScheduler().runTask(plugin, () -> { + ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.SELL, finalNewAmount, newPrice); + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + CompletableFuture.runAsync(() -> { + econ.withdrawPlayer(executor, worldName, newPrice * (100d - tax) / 100d); + econ.depositPlayer(shop.getVendor(), worldName, newPrice); + plugin.debug("Sell event cancelled (#" + shop.getID() + ")"); + }, plugin.getShopCreationThreadPool()); + return; + } + + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.SELL, null); + + addToInventory(inventory, newProduct); + removeFromInventory(executor.getInventory(), newProduct); + executor.updateInventory(); + + new BukkitRunnable() { + @Override + public void run() { + if (plugin.getHologramFormat().isDynamic()) { + shop.updateHologramText(); + } + } + }.runTaskLater(plugin, 1L); + + String vendorName = (shop.getVendor().getName() == null ? shop.getVendor().getUniqueId().toString() : shop.getVendor().getName()); + executor.sendMessage(LanguageUtils.getMessage(Message.SELL_SUCCESS, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)), + new Replacement(Placeholder.VENDOR, vendorName))); + + plugin.debug(executor.getName() + " successfully sold (#" + shop.getID() + ")"); + plugin.getLogger().info(String.format("%s sold %d of %s from %s", executor.getName(), finalNewAmount, newProduct.getItemStack().toString(), vendorName)); + + if (shop.getVendor().isOnline() && Config.enableVendorMessages) { + shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.SOMEONE_SOLD, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)), + new Replacement(Placeholder.PLAYER, executor.getName()))); + } else if (!shop.getVendor().isOnline() && Config.enableVendorBungeeMessages) { + String message = LanguageUtils.getMessage(Message.SOMEONE_SOLD, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)), + new Replacement(Placeholder.PLAYER, executor.getName())); + sendBungeeMessage(shop.getVendor().getName(), message); + } + }); + + } else { + CompletableFuture.runAsync(() -> { + plugin.debug("Economy transaction failed (r2): " + r2.errorMessage + " (#" + shop.getID() + ")"); + executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r2.errorMessage))); + econ.withdrawPlayer(executor, worldName, newPrice * (100d - tax) / 100d); + econ.depositPlayer(shop.getVendor(), worldName, newPrice); + }, plugin.getShopCreationThreadPool()); + } + + } else { + ShopBuySellEvent event = new ShopBuySellEvent(executor, shop, ShopBuySellEvent.Type.SELL, finalNewAmount, newPrice); + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + CompletableFuture.runAsync(() -> { + econ.withdrawPlayer(executor, worldName, newPrice * (100d - tax) / 100d); + plugin.debug("Sell event cancelled (#" + shop.getID() + ")"); + }, plugin.getShopCreationThreadPool()); + return; + } + + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.SELL, null); + + removeFromInventory(executor.getInventory(), newProduct); + executor.updateInventory(); + + new BukkitRunnable() { + @Override + public void run() { + if (plugin.getHologramFormat().isDynamic()) { + shop.updateHologramText(); + } + } + }.runTaskLater(plugin, 1L); + + executor.sendMessage(LanguageUtils.getMessage(Message.SELL_SUCCESS_ADMIN, new Replacement(Placeholder.AMOUNT, String.valueOf(finalNewAmount)), + new Replacement(Placeholder.ITEM_NAME, newProduct.getLocalizedName()), new Replacement(Placeholder.SELL_PRICE, String.valueOf(newPrice)))); + + plugin.debug(executor.getName() + " successfully sold (#" + shop.getID() + ")"); + plugin.getLogger().info(String.format("%s bought %d of %s from %s", executor.getName(), finalNewAmount, newProduct.getItemStack().toString(), "ADMIN")); + } + + } else { + CompletableFuture.runAsync(() -> { + plugin.debug("Economy transaction failed (r): " + r.errorMessage + " (#" + shop.getID() + ")"); + executor.sendMessage(LanguageUtils.getMessage(Message.ERROR_OCCURRED, new Replacement(Placeholder.ERROR, r.errorMessage))); + econ.withdrawPlayer(executor, worldName, newPrice); + }, plugin.getShopCreationThreadPool()); + } + }, plugin.getShopCreationThreadPool()); + + } else { + executor.sendMessage(LanguageUtils.getMessage(Message.CHEST_NOT_ENOUGH_INVENTORY_SPACE)); + } + }); + } else { + executor.sendMessage(LanguageUtils.getMessage(Message.VENDOR_NOT_ENOUGH_MONEY)); + } + }, plugin.getShopCreationThreadPool()); } /** * Adds items to an inventory * @param inventory The inventory, to which the items will be added - * @param itemStack Items to add + * @param product Products to add * @return Whether all items were added to the inventory */ private boolean addToInventory(Inventory inventory, ShopProduct product) { @@ -1033,7 +1113,7 @@ public class ShopInteractListener implements Listener { /** * Removes items to from an inventory * @param inventory The inventory, from which the items will be removed - * @param itemStack Items to remove + * @param product Products to remove * @return Whether all items were removed from the inventory */ private boolean removeFromInventory(Inventory inventory, ShopProduct product) { diff --git a/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java b/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java index 13679b1..874f01f 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java @@ -5,6 +5,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.ShulkerBox; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -16,6 +17,8 @@ import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.world.StructureGrowEvent; @@ -26,9 +29,11 @@ import de.epiceric.shopchest.utils.ShopUtils; public class ShopItemListener implements Listener { private ShopUtils shopUtils; + private final ShopChest plugin; public ShopItemListener(ShopChest plugin) { this.shopUtils = plugin.getShopUtils(); + this.plugin = plugin; } @EventHandler(priority = EventPriority.HIGH) @@ -60,6 +65,29 @@ public class ShopItemListener implements Listener { } } + @EventHandler(priority = EventPriority.HIGH) + public void onShulkerClose(InventoryCloseEvent e) { + if (e.getInventory().getType() != InventoryType.SHULKER_BOX) { + return; + } + + Block block = e.getPlayer().getTargetBlockExact(7); + + if (block == null || !(block.getState() instanceof ShulkerBox)) { + return; + } + + if (!shopUtils.isShop(block.getLocation())) { + return; + } + + Shop shop = shopUtils.getShop(block.getLocation()); + + Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> { + Bukkit.getOnlinePlayers().forEach(player -> shop.getItem().resetForPlayer(player)); + }, 15); + } + @EventHandler(priority = EventPriority.HIGH) public void onPistonExtend(BlockPistonExtendEvent e) { // If the piston would only move itself diff --git a/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java b/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java index 449713d..edd8036 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java @@ -5,8 +5,7 @@ import java.util.Set; import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.block.Chest; -import org.bukkit.block.DoubleChest; +import org.bukkit.block.*; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -37,12 +36,12 @@ public class ShopUpdateListener implements Listener { Location loc = null; - if (e.getSource().getHolder() instanceof Chest) { - loc = ((Chest) e.getSource().getHolder()).getLocation(); + if (e.getSource().getHolder() instanceof Chest || e.getSource().getHolder() instanceof ShulkerBox || e.getSource().getHolder() instanceof Barrel) { + loc = ((BlockState) e.getSource().getHolder()).getLocation(); } else if (e.getSource().getHolder() instanceof DoubleChest) { loc = ((DoubleChest) e.getSource().getHolder()).getLocation(); - } else if (e.getDestination().getHolder() instanceof Chest) { - loc = ((Chest) e.getDestination().getHolder()).getLocation(); + } else if (e.getDestination().getHolder() instanceof Chest || e.getDestination().getHolder() instanceof ShulkerBox || e.getDestination().getHolder() instanceof Barrel) { + loc = ((BlockState) e.getDestination().getHolder()).getLocation(); } else if (e.getDestination().getHolder() instanceof DoubleChest) { loc = ((DoubleChest) e.getDestination().getHolder()).getLocation(); } diff --git a/plugin/src/main/java/de/epiceric/shopchest/listeners/WorldGuardListener.java b/plugin/src/main/java/de/epiceric/shopchest/listeners/WorldGuardListener.java index b81aef3..e37e637 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/listeners/WorldGuardListener.java +++ b/plugin/src/main/java/de/epiceric/shopchest/listeners/WorldGuardListener.java @@ -2,10 +2,10 @@ package de.epiceric.shopchest.listeners; import java.util.Optional; +import de.epiceric.shopchest.utils.ShopUtils; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Chest; +import org.bukkit.block.*; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; @@ -69,7 +69,7 @@ public class WorldGuardListener implements Listener { Block block = event.getBlocks().get(0); Material type = block.getType(); - if (type == Material.CHEST || type == Material.TRAPPED_CHEST) { + if (ShopUtils.isShopMaterial(type)) { if (isAllowed(player, block.getLocation())) { event.setResult(Result.ALLOW); } @@ -77,8 +77,8 @@ public class WorldGuardListener implements Listener { } else if (event.getOriginalEvent() instanceof InventoryOpenEvent) { InventoryOpenEvent orig = (InventoryOpenEvent) event.getOriginalEvent(); - if (orig.getInventory().getHolder() instanceof Chest) { - if (isAllowed(player, ((Chest) orig.getInventory().getHolder()).getLocation())) { + if (orig.getInventory().getHolder() instanceof Chest || orig.getInventory().getHolder() instanceof ShulkerBox || orig.getInventory().getHolder() instanceof Barrel) { + if (isAllowed(player, ((BlockState) orig.getInventory().getHolder()).getLocation())) { event.setResult(Result.ALLOW); } } diff --git a/plugin/src/main/java/de/epiceric/shopchest/shop/Shop.java b/plugin/src/main/java/de/epiceric/shopchest/shop/Shop.java index 7ebe88e..35d5991 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/shop/Shop.java +++ b/plugin/src/main/java/de/epiceric/shopchest/shop/Shop.java @@ -5,16 +5,15 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; +import de.epiceric.shopchest.utils.ShopUtils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Chest; -import org.bukkit.block.DoubleChest; +import org.bukkit.block.*; import org.bukkit.block.data.Directional; import org.bukkit.entity.Player; +import org.bukkit.inventory.BlockInventoryHolder; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -40,10 +39,10 @@ public class Shop { private static class PreCreateResult { private final Inventory inventory; - private final Chest[] chests; + private final BlockInventoryHolder[] chests; private final BlockFace face; - private PreCreateResult(Inventory inventory, Chest[] chests, BlockFace face) { + private PreCreateResult(Inventory inventory, BlockInventoryHolder[] chests, BlockFace face) { this.inventory = inventory; this.chests = chests; this.face = face; @@ -113,7 +112,7 @@ public class Shop { plugin.debug("Creating shop (#" + id + ")"); Block b = location.getBlock(); - if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) { + if (!ShopUtils.isShopMaterial(b.getType())) { ChestNotFoundException ex = new ChestNotFoundException(String.format("No Chest found in world '%s' at location: %d; %d; %d", b.getWorld().getName(), b.getX(), b.getY(), b.getZ())); plugin.getShopUtils().removeShop(this, Config.removeShopOnError); @@ -176,7 +175,7 @@ public class Shop { /** *

Creates the floating item of the shop

- * Call this after {@link #createHologram()}, because it depends on the hologram's location + * Call this after {@link #createHologram(PreCreateResult)}, because it depends on the hologram's location */ private void createItem() { plugin.debug("Creating item (#" + id + ")"); @@ -198,7 +197,7 @@ public class Shop { if (ih == null) return null; - Chest[] chests = new Chest[2]; + BlockInventoryHolder[] chests = new BlockInventoryHolder[2]; BlockFace face; if (ih instanceof DoubleChest) { @@ -209,13 +208,23 @@ public class Shop { chests[0] = r; chests[1] = l; } else { - chests[0] = (Chest) ih; + chests[0] = (BlockInventoryHolder) ih; } if (Utils.getMajorVersion() < 13) { - face = ((org.bukkit.material.Directional) chests[0].getData()).getFacing(); + if (chests[0] instanceof Chest) { + Chest chest = (Chest) chests[0]; + face = ((org.bukkit.material.Directional) chest.getData()).getFacing(); + } else { + face = null; + } } else { - face = ((Directional) chests[0].getBlockData()).getFacing(); + if (chests[0] instanceof Chest) { + Chest chest = (Chest) chests[0]; + face = ((Directional) chest.getBlockData()).getFacing(); + } else { + face = null; + } } return new PreCreateResult(ih.getInventory(), chests, face); @@ -328,7 +337,7 @@ public class Shop { return lines.toArray(new String[0]); } - private Location getHologramLocation(Chest[] chests, BlockFace face) { + private Location getHologramLocation(BlockInventoryHolder[] chests, BlockFace face) { World w = location.getWorld(); int x = location.getBlockX(); int y = location.getBlockY(); @@ -341,21 +350,21 @@ public class Shop { if (Config.hologramFixedBottom) deltaY = -0.85; if (chests[1] != null) { - Chest c1 = Utils.getMajorVersion() >= 13 && (face == BlockFace.NORTH || face == BlockFace.EAST) ? chests[1] : chests[0]; - Chest c2 = Utils.getMajorVersion() >= 13 && (face == BlockFace.NORTH || face == BlockFace.EAST) ? chests[0] : chests[1]; + BlockInventoryHolder c1 = Utils.getMajorVersion() >= 13 && (face == BlockFace.NORTH || face == BlockFace.EAST) ? chests[1] : chests[0]; + BlockInventoryHolder c2 = Utils.getMajorVersion() >= 13 && (face == BlockFace.NORTH || face == BlockFace.EAST) ? chests[0] : chests[1]; - if (holoLocation.equals(c1.getLocation())) { - if (c1.getX() != c2.getX()) { + if (holoLocation.equals(c1.getBlock().getLocation())) { + if (c1.getBlock().getX() != c2.getBlock().getX()) { holoLocation.add(0, deltaY, 0.5); - } else if (c1.getZ() != c2.getZ()) { + } else if (c1.getBlock().getZ() != c2.getBlock().getZ()) { holoLocation.add(0.5, deltaY, 0); } else { holoLocation.add(0.5, deltaY, 0.5); } } else { - if (c1.getX() != c2.getX()) { + if (c1.getBlock().getX() != c2.getBlock().getX()) { holoLocation.add(1, deltaY, 0.5); - } else if (c1.getZ() != c2.getZ()) { + } else if (c1.getBlock().getZ() != c2.getBlock().getZ()) { holoLocation.add(0.5, deltaY, 1); } else { holoLocation.add(0.5, deltaY, 0.5); @@ -472,9 +481,9 @@ public class Shop { public InventoryHolder getInventoryHolder() { Block b = getLocation().getBlock(); - if (b.getType() == Material.CHEST || b.getType() == Material.TRAPPED_CHEST) { - Chest chest = (Chest) b.getState(); - return chest.getInventory().getHolder(); + if (ShopUtils.isShopMaterial(b.getType())) { + Container container = (Container) b.getState(); + return container.getInventory().getHolder(); } return null; diff --git a/plugin/src/main/java/de/epiceric/shopchest/sql/MySQL.java b/plugin/src/main/java/de/epiceric/shopchest/sql/MySQL.java index 3a6df4e..daada99 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/sql/MySQL.java +++ b/plugin/src/main/java/de/epiceric/shopchest/sql/MySQL.java @@ -21,7 +21,9 @@ public class MySQL extends Database { @Override HikariDataSource getDataSource() { HikariConfig config = new HikariConfig(); - config.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s?autoReconnect=true&useSSL=false&serverTimezone=UTC", + // TODO Inspect this + // Why mariadb instead of my sql dani ? + config.setJdbcUrl(String.format("jdbc:mariadb://%s:%d/%s", Config.databaseMySqlHost, Config.databaseMySqlPort, Config.databaseMySqlDatabase)); config.setUsername(Config.databaseMySqlUsername); config.setPassword(Config.databaseMySqlPassword); diff --git a/plugin/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java b/plugin/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java index 8944077..adbcc03 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java +++ b/plugin/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java @@ -11,10 +11,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.OfflinePlayer; +import org.bukkit.*; import org.bukkit.block.Chest; import org.bukkit.block.DoubleChest; import org.bukkit.entity.Player; @@ -107,7 +104,7 @@ public class ShopUtils { shopLocation.put(r.getLocation(), shop); shopLocation.put(l.getLocation(), shop); } else { - plugin.debug("Added shop as single chest. (#" + shop.getID() + ")"); + plugin.debug("Added shop as single container. (#" + shop.getID() + ")"); shopLocation.put(shop.getLocation(), shop); } @@ -320,6 +317,33 @@ public class ShopUtils { }); } + /* + * Tells if the material given is a shop material + * @param material The material to test + */ + public static boolean isShopMaterial(Material material) { + return material.equals(Material.CHEST) || + material.equals(Material.TRAPPED_CHEST) || + material.equals(Material.BARREL) || + material.equals(Material.SHULKER_BOX) || + material.equals(Material.BLACK_SHULKER_BOX) || + material.equals(Material.BLUE_SHULKER_BOX) || + material.equals(Material.BROWN_SHULKER_BOX) || + material.equals(Material.CYAN_SHULKER_BOX) || + material.equals(Material.GRAY_SHULKER_BOX) || + material.equals(Material.GREEN_SHULKER_BOX) || + material.equals(Material.LIGHT_BLUE_SHULKER_BOX) || + material.equals(Material.LIGHT_GRAY_SHULKER_BOX) || + material.equals(Material.LIME_SHULKER_BOX) || + material.equals(Material.MAGENTA_SHULKER_BOX) || + material.equals(Material.ORANGE_SHULKER_BOX) || + material.equals(Material.PINK_SHULKER_BOX) || + material.equals(Material.PURPLE_SHULKER_BOX) || + material.equals(Material.RED_SHULKER_BOX) || + material.equals(Material.WHITE_SHULKER_BOX) || + material.equals(Material.YELLOW_SHULKER_BOX); + } + /** * Loads the amount of shops for each player * @param callback Callback that returns the amount of shops for each player diff --git a/plugin/src/main/java/de/epiceric/shopchest/utils/UpdateChecker.java b/plugin/src/main/java/de/epiceric/shopchest/utils/UpdateChecker.java index 96715aa..bafa53f 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/utils/UpdateChecker.java +++ b/plugin/src/main/java/de/epiceric/shopchest/utils/UpdateChecker.java @@ -1,15 +1,14 @@ package de.epiceric.shopchest.utils; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; - import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; - import de.epiceric.shopchest.ShopChest; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; + public class UpdateChecker { private ShopChest plugin; @@ -36,7 +35,7 @@ public class UpdateChecker { conn.setRequestProperty("User-Agent", "ShopChest/UpdateChecker"); InputStreamReader reader = new InputStreamReader(conn.getInputStream()); - JsonElement element = new JsonParser().parse(reader); + JsonElement element = JsonParser.parseReader(reader); if (element.isJsonArray()) { JsonObject result = element.getAsJsonArray().get(0).getAsJsonObject(); @@ -45,11 +44,11 @@ public class UpdateChecker { link = "https://www.spigotmc.org/resources/shopchest.11431/download?version=" + id; } else { plugin.debug("Failed to check for updates"); - plugin.debug("Result: " + element.toString()); + plugin.debug("Result: " + element); return UpdateCheckerResult.ERROR; } - if (plugin.getDescription().getVersion().equals(version)) { + if (compareVersion(version) == 1) { plugin.debug("No update found"); return UpdateCheckerResult.FALSE; } else { @@ -64,6 +63,28 @@ public class UpdateChecker { } } + private int compareVersion(String version) { + String[] t = plugin.getDescription().getVersion().split("\\-")[0].split("\\."); + String[] o = version.split("\\-")[0].split("\\."); + int[] t1 = new int[t.length]; + int[] o1 = new int[o.length]; + for (int i = 0; i < t.length; i++) { + t1[i] = Integer.parseInt(t[i]); + } + for (int i = 0; i < o.length; i++) { + o1[i] = Integer.parseInt(o[i]); + } + final int maxLength = Math.max(t1.length, o1.length); + for (int i = 0; i < maxLength; i++) { + final int left = i < t1.length ? t1[i] : 0; + final int right = i < o1.length ? o1[i] : 0; + if (left != right) { + return left < right ? -1 : 1; + } + } + return 0; + } + /** * @return Latest Version or null if no update is available */ diff --git a/plugin/src/main/java/de/epiceric/shopchest/utils/Utils.java b/plugin/src/main/java/de/epiceric/shopchest/utils/Utils.java index a64bde9..3b95fa1 100644 --- a/plugin/src/main/java/de/epiceric/shopchest/utils/Utils.java +++ b/plugin/src/main/java/de/epiceric/shopchest/utils/Utils.java @@ -10,16 +10,21 @@ import de.epiceric.shopchest.shop.Shop; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.block.Beehive; import org.bukkit.block.Chest; import org.bukkit.block.DoubleChest; +import org.bukkit.block.ShulkerBox; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.ItemMeta; import java.nio.charset.StandardCharsets; @@ -43,10 +48,7 @@ public class Utils { ItemMeta itemMeta1 = itemStack1.getItemMeta(); ItemMeta itemMeta2 = itemStack2.getItemMeta(); - if (itemMeta1 instanceof BookMeta && itemMeta2 instanceof BookMeta) { - BookMeta bookMeta1 = (BookMeta) itemStack1.getItemMeta(); - BookMeta bookMeta2 = (BookMeta) itemStack2.getItemMeta(); - + if (itemMeta1 instanceof BookMeta bookMeta1 && itemMeta2 instanceof BookMeta bookMeta2) { if ((getMajorVersion() == 9 && getRevision() == 1) || getMajorVersion() == 8) { CustomBookMeta.Generation generation1 = CustomBookMeta.getGeneration(itemStack1); CustomBookMeta.Generation generation2 = CustomBookMeta.getGeneration(itemStack2); @@ -65,6 +67,50 @@ public class Utils { itemStack2 = decode(encode(itemStack2)); } + if (itemMeta1 instanceof BlockStateMeta && itemMeta2 instanceof BlockStateMeta) { + BlockStateMeta blockMeta1 = (BlockStateMeta)itemMeta1; + BlockStateMeta blockMeta2 = (BlockStateMeta)itemMeta2; + + if (blockMeta1.getBlockState() instanceof ShulkerBox box1 && blockMeta2.getBlockState() instanceof ShulkerBox box2) { + + if (!box1.getInventory().isEmpty() && !box2.getInventory().isEmpty()) { + return false; + } + } + } + + if (itemMeta1 instanceof EnchantmentStorageMeta book1 && itemMeta2 instanceof EnchantmentStorageMeta book2) { + + if (book1.hasStoredEnchants() != book2.hasStoredEnchants()) { + return false; + } + + for (Map.Entry enchantment: book1.getStoredEnchants().entrySet()) { + if (!book2.hasStoredEnchant(enchantment.getKey())) { + return false; + } + if (book2.getStoredEnchantLevel(enchantment.getKey()) != enchantment.getValue()) { + return false; + } + } + + //Cross-check for set equivalence + for (Map.Entry enchantment: book2.getStoredEnchants().entrySet()) { + if (!book1.hasStoredEnchant(enchantment.getKey())) { + return false; + } + if (book1.getStoredEnchantLevel(enchantment.getKey()) != enchantment.getValue()) { + return false; + } + } + + return true; + } + + if (itemMeta1 instanceof Beehive b1 && itemMeta2 instanceof Beehive b2) { + return b1.getEntityCount() == b2.getEntityCount(); + } + return itemStack1.isSimilar(itemStack2); } @@ -212,7 +258,7 @@ public class Utils { if (Utils.getMajorVersion() < 13) axes = Arrays.asList("WOOD_AXE", "STONE_AXE", "IRON_AXE", "GOLD_AXE", "DIAMOND_AXE"); else - axes = Arrays.asList("WOODEN_AXE", "STONE_AXE", "IRON_AXE", "GOLDEN_AXE", "DIAMOND_AXE"); + axes = Arrays.asList("WOODEN_AXE", "STONE_AXE", "IRON_AXE", "GOLDEN_AXE", "DIAMOND_AXE", "NETHERITE_AXE"); ItemStack item = getItemInMainHand(p); if (item == null || !axes.contains(item.getType().toString())) { diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index f00e5d5..32034ee 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -147,6 +147,11 @@ shop-creation-price: # ...an admin shop admin: 0 +shop-tax: + default: 0 + enchanted_book: 30 + + # Shop limits are handled with permissions. # A player with permission "shopchest.limit.X" has a limit of X shops, # a player with permission "shopchest.limit.*" does not have a shop limit. diff --git a/pom.xml b/pom.xml index 04f9a33..c3a4595 100644 --- a/pom.xml +++ b/pom.xml @@ -57,13 +57,20 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 defaultVersion + + + maven-snapshots + https://repository.apache.org/content/repositories/snapshots/ + + + @@ -120,7 +127,7 @@ com.plotsquared PlotSquared-Core - 6.5.0 + 6.5.1 provided @@ -271,11 +278,6 @@ worldguardwrapper 1.2.0-SNAPSHOT - - org.bstats - bstats-bukkit - 2.2.1 - com.zaxxer HikariCP @@ -289,7 +291,7 @@ org.inventivetalent reflectionhelper - 1.18.4-SNAPSHOT + 1.18.7-SNAPSHOT @@ -332,7 +334,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.2 + 3.3.1-SNAPSHOT package @@ -344,10 +346,6 @@ false - - org.bstats - de.epiceric.shopchest.dependencies.bstats - org.codemc.worldguardwrapper de.epiceric.shopchest.dependencies.worldguardwrapper @@ -364,6 +362,32 @@ org.inventivetalent.reflection de.epiceric.shopchest.dependencies.reflectionhelper + + + + com.google.common + de.epiceric.shopchest.dependencies.google-common + + + com.google.errorprone + de.epiceric.shopchest.dependencies.google-errorprone + + + com.google.gson + de.epiceric.shopchest.dependencies.google-gson + + + com.google.j2objc + de.epiceric.shopchest.dependencies.google-j2objc + + + com.google.thirdparty + de.epiceric.shopchest.dependencies.google-thirdparty + + + javax.annotation + de.epiceric.shopchest.dependencies.javax-annotation +