From 6ae12f65c092d63ed2b1dbe8fcd2dbb1b2e8366d Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 10 Nov 2018 20:34:11 +0100 Subject: [PATCH] Database improvements - Support table prefixes (Fixes #138) - Support amounts above 127 in Paper (Fixes #149) - Split UUID, name and shop type in economy log - Split product and amount in economy log (Fixes #143) - Added product data (Base64) to economy log --- .../command/ShopCommandExecutor.java | 9 +- .../de/epiceric/shopchest/config/Config.java | 8 +- .../listeners/NotifyPlayerOnJoinListener.java | 3 +- .../listeners/ShopInteractListener.java | 63 +-- .../java/de/epiceric/shopchest/shop/Shop.java | 56 ++- .../epiceric/shopchest/shop/ShopProduct.java | 34 ++ .../de/epiceric/shopchest/sql/Database.java | 373 +++++++++++------- .../epiceric/shopchest/utils/ClickType.java | 8 +- src/main/resources/config.yml | 3 + 9 files changed, 342 insertions(+), 215 deletions(-) create mode 100644 src/main/java/de/epiceric/shopchest/shop/ShopProduct.java diff --git a/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java b/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java index 730f37d..d99ac48 100644 --- a/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java +++ b/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java @@ -13,6 +13,7 @@ import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.language.Message; import de.epiceric.shopchest.language.Replacement; import de.epiceric.shopchest.shop.Shop; +import de.epiceric.shopchest.shop.ShopProduct; import de.epiceric.shopchest.utils.Callback; import de.epiceric.shopchest.utils.ClickType; import de.epiceric.shopchest.utils.ItemUtils; @@ -354,11 +355,8 @@ class ShopCommandExecutor implements CommandExecutor { } } - ItemStack product = new ItemStack(inHand.getType(), amount, inHand.getDurability()); - product.setItemMeta(inHand.getItemMeta()); - - if (Enchantment.DURABILITY.canEnchantItem(product)) { - if (product.getDurability() > 0 && !Config.allowBrokenItems) { + if (Enchantment.DURABILITY.canEnchantItem(inHand)) { + if (inHand.getDurability() > 0 && !Config.allowBrokenItems) { p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_BROKEN_ITEM)); plugin.debug(p.getName() + "'s item is broken"); return; @@ -374,6 +372,7 @@ class ShopCommandExecutor implements CommandExecutor { } } + ShopProduct product = new ShopProduct(inHand, amount); ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, product, null, buyPrice, sellPrice, shopType)); Bukkit.getPluginManager().callEvent(event); diff --git a/src/main/java/de/epiceric/shopchest/config/Config.java b/src/main/java/de/epiceric/shopchest/config/Config.java index f3f2a13..402a8e1 100644 --- a/src/main/java/de/epiceric/shopchest/config/Config.java +++ b/src/main/java/de/epiceric/shopchest/config/Config.java @@ -87,7 +87,12 @@ public class Config { public static String databaseMySqlPassword; /** - * The database type used for ShopChest. + * The prefix to be used for database tables + */ + public static String databaseTablePrefix; + + /** + * The database type used for ShopChest **/ public static Database.DatabaseType databaseType; @@ -474,6 +479,7 @@ public class Config { databaseMySqlDatabase = plugin.getConfig().getString("database.mysql.database"); databaseMySqlUsername = plugin.getConfig().getString("database.mysql.username"); databaseMySqlPassword = plugin.getConfig().getString("database.mysql.password"); + databaseTablePrefix = plugin.getConfig().getString("database.table-prefix"); 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); diff --git a/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java b/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java index a0bb1a1..eab4345 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java @@ -57,8 +57,7 @@ public class NotifyPlayerOnJoinListener implements Listener { @EventHandler public void onPlayerQuit(PlayerQuitEvent e) { - long time = System.currentTimeMillis(); - plugin.getShopDatabase().logLogout(e.getPlayer(), time, null); + plugin.getShopDatabase().logLogout(e.getPlayer(), null); } } diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index 1077318..9b08f30 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -24,6 +24,7 @@ import de.epiceric.shopchest.language.Replacement; import de.epiceric.shopchest.nms.Hologram; import de.epiceric.shopchest.nms.JsonBuilder; import de.epiceric.shopchest.shop.Shop; +import de.epiceric.shopchest.shop.ShopProduct; import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.sql.Database; import de.epiceric.shopchest.utils.ClickType; @@ -301,7 +302,7 @@ public class ShopInteractListener implements Listener { if (b.getRelative(BlockFace.UP).getType() == Material.AIR) { ClickType clickType = ClickType.getPlayerClickType(p); - ItemStack product = clickType.getProduct(); + ShopProduct product = clickType.getProduct(); double buyPrice = clickType.getBuyPrice(); double sellPrice = clickType.getSellPrice(); ShopType shopType = clickType.getShopType(); @@ -484,9 +485,10 @@ public class ShopInteractListener implements Listener { } else { if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) { Chest c = (Chest) b.getState(); - int amount = (p.isSneaking() ? shop.getProduct().getMaxStackSize() : shop.getProduct().getAmount()); + ItemStack itemStack = shop.getProduct().getItemStack(); + int amount = (p.isSneaking() ? itemStack.getMaxStackSize() : shop.getProduct().getAmount()); - if (Utils.getAmount(c.getInventory(), shop.getProduct()) >= amount) { + if (Utils.getAmount(c.getInventory(), itemStack) >= amount) { if (confirmed || !Config.confirmShopping) { buy(p, shop, p.isSneaking()); if (Config.confirmShopping) { @@ -503,7 +505,7 @@ public class ShopInteractListener implements Listener { needsConfirmation.put(p.getUniqueId(), ids); } } else { - if (Config.autoCalculateItemAmount && Utils.getAmount(c.getInventory(), shop.getProduct()) > 0) { + if (Config.autoCalculateItemAmount && Utils.getAmount(c.getInventory(), itemStack) > 0) { if (confirmed || !Config.confirmShopping) { buy(p, shop, p.isSneaking()); if (Config.confirmShopping) { @@ -524,7 +526,7 @@ public class ShopInteractListener implements Listener { if (shop.getVendor().isOnline() && Config.enableVendorMessages) { shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.VENDOR_OUT_OF_STOCK, new Replacement(Placeholder.AMOUNT, String.valueOf(shop.getProduct().getAmount())), - new Replacement(Placeholder.ITEM_NAME, LanguageUtils.getItemName(shop.getProduct())))); + new Replacement(Placeholder.ITEM_NAME, LanguageUtils.getItemName(itemStack)))); } plugin.debug("Shop is out of stock"); } @@ -569,11 +571,13 @@ public class ShopInteractListener implements Listener { externalPluginsAllowed = WorldGuardWrapper.getInstance().queryStateFlag(p, b.getLocation(), flagName).orElse(false); } + ItemStack itemStack = shop.getProduct().getItemStack(); + if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) { boolean stack = p.isSneaking() && !Utils.hasAxeInHand(p); - int amount = stack ? shop.getProduct().getMaxStackSize() : shop.getProduct().getAmount(); + int amount = stack ? itemStack.getMaxStackSize() : shop.getProduct().getAmount(); - if (Utils.getAmount(p.getInventory(), shop.getProduct()) >= amount) { + if (Utils.getAmount(p.getInventory(), itemStack) >= amount) { if (confirmed || !Config.confirmShopping) { sell(p, shop, stack); if (Config.confirmShopping) { @@ -590,7 +594,7 @@ public class ShopInteractListener implements Listener { needsConfirmation.put(p.getUniqueId(), ids); } } else { - if (Config.autoCalculateItemAmount && Utils.getAmount(p.getInventory(), shop.getProduct()) > 0) { + if (Config.autoCalculateItemAmount && Utils.getAmount(p.getInventory(), itemStack) > 0) { if (confirmed || !Config.confirmShopping) { sell(p, shop, stack); if (Config.confirmShopping) { @@ -716,7 +720,7 @@ public class ShopInteractListener implements Listener { * @param sellPrice Sell price * @param shopType Type of the shop */ - private void create(final Player executor, final Location location, final ItemStack product, final double buyPrice, final double sellPrice, final ShopType shopType) { + private void create(final Player executor, final Location location, final ShopProduct product, final double buyPrice, final double sellPrice, final ShopType shopType) { plugin.debug(executor.getName() + " is creating new shop..."); if (!executor.hasPermission(Permissions.CREATE)) { @@ -825,7 +829,8 @@ public class ShopInteractListener implements Listener { } Chest c = (Chest) shop.getLocation().getBlock().getState(); - int amount = Utils.getAmount(c.getInventory(), shop.getProduct()); + ItemStack itemStack = shop.getProduct().getItemStack(); + int amount = Utils.getAmount(c.getInventory(), itemStack); String vendorName = (shop.getVendor().getName() == null ? shop.getVendor().getUniqueId().toString() : shop.getVendor().getName()); @@ -863,21 +868,21 @@ public class ShopInteractListener implements Listener { * @param product The product of the shop * @return A {@link JsonBuilder} that can send the message via {@link JsonBuilder#sendJson(Player)} */ - private JsonBuilder getProductJson(ItemStack product) { + private JsonBuilder getProductJson(ShopProduct product) { // Add spaces at start and end, so there will always be a part before and after // the item name after splitting at Placeholder.ITEM_NAME String productString = " " + LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT, new Replacement(Placeholder.AMOUNT, String.valueOf(product.getAmount()))) + " "; String[] parts = productString.split(Placeholder.ITEM_NAME.toString()); - String productName = LanguageUtils.getItemName(product); + String productName = LanguageUtils.getItemName(product.getItemStack()); String jsonItem = ""; JsonBuilder jb = new JsonBuilder(plugin); JsonBuilder.PartArray rootArray = new JsonBuilder.PartArray(); try { Class craftItemStackClass = Utils.getCraftClass("inventory.CraftItemStack"); - Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, product); + Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, product.getItemStack()); Class nbtTagCompoundClass = Utils.getNMSClass("NBTTagCompound"); Object nbtTagCompound = nbtTagCompoundClass.getConstructor().newInstance(); nmsStack.getClass().getMethod("save", nbtTagCompoundClass).invoke(nmsStack, nbtTagCompound); @@ -944,8 +949,9 @@ public class ShopInteractListener implements Listener { private void buy(Player executor, final Shop shop, boolean stack) { plugin.debug(executor.getName() + " is buying (#" + shop.getID() + ")"); + ItemStack itemStack = shop.getProduct().getItemStack(); int amount = shop.getProduct().getAmount(); - if (stack) amount = shop.getProduct().getMaxStackSize(); + if (stack) amount = itemStack.getMaxStackSize(); String worldName = shop.getLocation().getWorld().getName(); @@ -966,14 +972,14 @@ public class ShopInteractListener implements Listener { Block b = shop.getLocation().getBlock(); Chest c = (Chest) b.getState(); - int amountForChestItems = Utils.getAmount(c.getInventory(), shop.getProduct()); + 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(shop.getProduct()); + ItemStack product = new ItemStack(itemStack); if (stack) product.setAmount(amount); Inventory inventory = executor.getInventory(); @@ -996,14 +1002,12 @@ public class ShopInteractListener implements Listener { 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() + ")"); - ItemStack newProduct = new ItemStack(product); - newProduct.setAmount(newAmount); - EconomyResponse r = econ.withdrawPlayer(executor, worldName, newPrice); if (r.transactionSuccess()) { @@ -1105,8 +1109,9 @@ public class ShopInteractListener implements Listener { private void sell(Player executor, final Shop shop, boolean stack) { plugin.debug(executor.getName() + " is selling (#" + shop.getID() + ")"); + ItemStack itemStack = shop.getProduct().getItemStack(); int amount = shop.getProduct().getAmount(); - if (stack) amount = shop.getProduct().getMaxStackSize(); + if (stack) amount = itemStack.getMaxStackSize(); double price = shop.getSellPrice(); if (stack) price = (price / shop.getProduct().getAmount()) * amount; @@ -1130,14 +1135,14 @@ public class ShopInteractListener implements Listener { Block block = shop.getLocation().getBlock(); Chest chest = (Chest) block.getState(); - int amountForItemCount = Utils.getAmount(executor.getInventory(), shop.getProduct()); + int amountForItemCount = Utils.getAmount(executor.getInventory(), itemStack); if (amountForItemCount == 0) { executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_ITEMS)); return; } - ItemStack product = new ItemStack(shop.getProduct()); + ItemStack product = new ItemStack(itemStack); if (stack) product.setAmount(amount); Inventory inventory = chest.getInventory(); @@ -1160,14 +1165,12 @@ public class ShopInteractListener implements Listener { 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() + ")"); - ItemStack newProduct = new ItemStack(product); - newProduct.setAmount(newAmount); - EconomyResponse r = econ.depositPlayer(executor, worldName, newPrice); if (r.transactionSuccess()) { @@ -1271,11 +1274,12 @@ public class ShopInteractListener implements Listener { * @param itemStack Items to add * @return Whether all items were added to the inventory */ - private boolean addToInventory(Inventory inventory, ItemStack itemStack) { + private boolean addToInventory(Inventory inventory, ShopProduct product) { plugin.debug("Adding items to inventory..."); HashMap inventoryItems = new HashMap<>(); - int amount = itemStack.getAmount(); + ItemStack itemStack = product.getItemStack(); + int amount = product.getAmount(); int added = 0; if (inventory instanceof PlayerInventory) { @@ -1329,11 +1333,12 @@ public class ShopInteractListener implements Listener { * @param itemStack Items to remove * @return Whether all items were removed from the inventory */ - private boolean removeFromInventory(Inventory inventory, ItemStack itemStack) { + private boolean removeFromInventory(Inventory inventory, ShopProduct product) { plugin.debug("Removing items from inventory..."); HashMap inventoryItems = new HashMap<>(); - int amount = itemStack.getAmount(); + ItemStack itemStack = product.getItemStack(); + int amount = product.getAmount(); int removed = 0; if (inventory instanceof PlayerInventory) { diff --git a/src/main/java/de/epiceric/shopchest/shop/Shop.java b/src/main/java/de/epiceric/shopchest/shop/Shop.java index 1507aeb..4f41c93 100644 --- a/src/main/java/de/epiceric/shopchest/shop/Shop.java +++ b/src/main/java/de/epiceric/shopchest/shop/Shop.java @@ -48,7 +48,7 @@ public class Shop { private final ShopChest plugin; private final OfflinePlayer vendor; - private final ItemStack product; + private final ShopProduct product; private final Location location; private final double buyPrice; private final double sellPrice; @@ -60,7 +60,7 @@ public class Shop { private Location holoLocation; private ShopItem item; - public Shop(int id, ShopChest plugin, OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) { + public Shop(int id, ShopChest plugin, OfflinePlayer vendor, ShopProduct product, Location location, double buyPrice, double sellPrice, ShopType shopType) { this.id = id; this.plugin = plugin; this.vendor = vendor; @@ -71,7 +71,7 @@ public class Shop { this.shopType = shopType; } - public Shop(ShopChest plugin, OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) { + public Shop(ShopChest plugin, OfflinePlayer vendor, ShopProduct product, Location location, double buyPrice, double sellPrice, ShopType shopType) { this(-1, plugin, vendor, product, location, buyPrice, sellPrice, shopType); } @@ -179,13 +179,9 @@ public class Shop { plugin.debug("Creating item (#" + id + ")"); Location itemLocation; - ItemStack itemStack; itemLocation = new Location(location.getWorld(), holoLocation.getX(), location.getY() + 0.9, holoLocation.getZ()); - itemStack = product.clone(); - itemStack.setAmount(1); - - item = new ShopItem(plugin, itemStack, itemLocation); + item = new ShopItem(plugin, product.getItemStack(), itemLocation); } } @@ -260,39 +256,41 @@ public class Shop { private String[] getHologramText(Inventory inventory) { List lines = new ArrayList<>(); + ItemStack itemStack = getProduct().getItemStack(); + Map requirements = new EnumMap<>(HologramFormat.Requirement.class); requirements.put(HologramFormat.Requirement.VENDOR, getVendor().getName()); requirements.put(HologramFormat.Requirement.AMOUNT, getProduct().getAmount()); - requirements.put(HologramFormat.Requirement.ITEM_TYPE, getProduct().getType() + (getProduct().getDurability() > 0 ? ":" + getProduct().getDurability() : "")); - requirements.put(HologramFormat.Requirement.ITEM_NAME, getProduct().hasItemMeta() ? getProduct().getItemMeta().getDisplayName() : null); - requirements.put(HologramFormat.Requirement.HAS_ENCHANTMENT, !LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(getProduct())).isEmpty()); + requirements.put(HologramFormat.Requirement.ITEM_TYPE, itemStack.getType() + (itemStack.getDurability() > 0 ? ":" + itemStack.getDurability() : "")); + requirements.put(HologramFormat.Requirement.ITEM_NAME, itemStack.hasItemMeta() ? itemStack.getItemMeta().getDisplayName() : null); + requirements.put(HologramFormat.Requirement.HAS_ENCHANTMENT, !LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(itemStack)).isEmpty()); requirements.put(HologramFormat.Requirement.BUY_PRICE, getBuyPrice()); requirements.put(HologramFormat.Requirement.SELL_PRICE, getSellPrice()); - requirements.put(HologramFormat.Requirement.HAS_POTION_EFFECT, ItemUtils.getPotionEffect(getProduct()) != null); - requirements.put(HologramFormat.Requirement.IS_MUSIC_DISC, getProduct().getType().isRecord()); - requirements.put(HologramFormat.Requirement.IS_POTION_EXTENDED, ItemUtils.isExtendedPotion(getProduct())); - requirements.put(HologramFormat.Requirement.IS_WRITTEN_BOOK, getProduct().getType() == Material.WRITTEN_BOOK); + requirements.put(HologramFormat.Requirement.HAS_POTION_EFFECT, ItemUtils.getPotionEffect(itemStack) != null); + requirements.put(HologramFormat.Requirement.IS_MUSIC_DISC, itemStack.getType().isRecord()); + requirements.put(HologramFormat.Requirement.IS_POTION_EXTENDED, ItemUtils.isExtendedPotion(itemStack)); + requirements.put(HologramFormat.Requirement.IS_WRITTEN_BOOK, itemStack.getType() == Material.WRITTEN_BOOK); requirements.put(HologramFormat.Requirement.ADMIN_SHOP, getShopType() == ShopType.ADMIN); requirements.put(HologramFormat.Requirement.NORMAL_SHOP, getShopType() == ShopType.NORMAL); - requirements.put(HologramFormat.Requirement.IN_STOCK, Utils.getAmount(inventory, getProduct())); - requirements.put(HologramFormat.Requirement.MAX_STACK, getProduct().getMaxStackSize()); - requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, getProduct())); - requirements.put(HologramFormat.Requirement.DURABILITY, getProduct().getDurability()); + requirements.put(HologramFormat.Requirement.IN_STOCK, Utils.getAmount(inventory, itemStack)); + requirements.put(HologramFormat.Requirement.MAX_STACK, itemStack.getMaxStackSize()); + requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, itemStack)); + requirements.put(HologramFormat.Requirement.DURABILITY, itemStack.getDurability()); Map placeholders = new EnumMap<>(Placeholder.class); placeholders.put(Placeholder.VENDOR, getVendor().getName()); placeholders.put(Placeholder.AMOUNT, getProduct().getAmount()); - placeholders.put(Placeholder.ITEM_NAME, LanguageUtils.getItemName(getProduct())); - placeholders.put(Placeholder.ENCHANTMENT, LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(getProduct()))); + placeholders.put(Placeholder.ITEM_NAME, LanguageUtils.getItemName(itemStack)); + placeholders.put(Placeholder.ENCHANTMENT, LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(itemStack))); placeholders.put(Placeholder.BUY_PRICE, getBuyPrice()); placeholders.put(Placeholder.SELL_PRICE, getSellPrice()); - placeholders.put(Placeholder.POTION_EFFECT, LanguageUtils.getPotionEffectName(getProduct())); - placeholders.put(Placeholder.MUSIC_TITLE, LanguageUtils.getMusicDiscName(getProduct().getType())); - placeholders.put(Placeholder.GENERATION, LanguageUtils.getBookGenerationName(getProduct())); - placeholders.put(Placeholder.STOCK, Utils.getAmount(inventory, getProduct())); - placeholders.put(Placeholder.MAX_STACK, getProduct().getMaxStackSize()); - placeholders.put(Placeholder.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, getProduct())); - placeholders.put(Placeholder.DURABILITY, getProduct().getDurability()); + placeholders.put(Placeholder.POTION_EFFECT, LanguageUtils.getPotionEffectName(itemStack)); + placeholders.put(Placeholder.MUSIC_TITLE, LanguageUtils.getMusicDiscName(itemStack.getType())); + placeholders.put(Placeholder.GENERATION, LanguageUtils.getBookGenerationName(itemStack)); + placeholders.put(Placeholder.STOCK, Utils.getAmount(inventory, itemStack)); + placeholders.put(Placeholder.MAX_STACK, itemStack.getMaxStackSize()); + placeholders.put(Placeholder.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, itemStack)); + placeholders.put(Placeholder.DURABILITY, itemStack.getDurability()); int lineCount = plugin.getHologramFormat().getLineCount(); @@ -407,7 +405,7 @@ public class Shop { /** * @return Product the shop sells (or buys) */ - public ItemStack getProduct() { + public ShopProduct getProduct() { return product; } diff --git a/src/main/java/de/epiceric/shopchest/shop/ShopProduct.java b/src/main/java/de/epiceric/shopchest/shop/ShopProduct.java new file mode 100644 index 0000000..19d78c2 --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/shop/ShopProduct.java @@ -0,0 +1,34 @@ +package de.epiceric.shopchest.shop; + +import org.bukkit.inventory.ItemStack; + +public class ShopProduct { + + private final ItemStack itemStack; + private final int amount; + + public ShopProduct(ItemStack itemStack, int amount) { + this.itemStack = new ItemStack(itemStack); + this.itemStack.setAmount(1); + this.amount = amount; + } + + public ShopProduct(ItemStack itemStack) { + this(itemStack, itemStack.getAmount()); + } + + /** + * @return The {@link ItemStack} with an amount of {@code 1}. + */ + public ItemStack getItemStack() { + return itemStack; + } + + /** + * @return The amount + */ + public int getAmount() { + return amount; + } + +} \ No newline at end of file diff --git a/src/main/java/de/epiceric/shopchest/sql/Database.java b/src/main/java/de/epiceric/shopchest/sql/Database.java index e4a58f7..fe155d0 100644 --- a/src/main/java/de/epiceric/shopchest/sql/Database.java +++ b/src/main/java/de/epiceric/shopchest/sql/Database.java @@ -7,6 +7,7 @@ import de.epiceric.shopchest.event.ShopBuySellEvent.Type; import de.epiceric.shopchest.exceptions.WorldNotFoundException; import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.shop.Shop; +import de.epiceric.shopchest.shop.ShopProduct; import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.utils.Callback; import de.epiceric.shopchest.utils.Utils; @@ -18,11 +19,14 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -35,6 +39,10 @@ public abstract class Database { private static Set notFoundWorlds = new HashSet<>(); + private String tableShops; + private String tableLogs; + private String tableLogouts; + ShopChest plugin; HikariDataSource dataSource; @@ -52,18 +60,31 @@ public abstract class Database { * that were found (as {@code int}) */ public void connect(final Callback callback) { + if (!Config.databaseTablePrefix.matches("^([a-zA-Z0-9\\-\\_]+)?$")) { + // Only letters, numbers dashes and underscores are allowed + plugin.getLogger().severe("Database table prefix contains illegal letters, using 'shopchest_' prefix."); + Config.databaseTablePrefix = "shopchest_"; + } + + this.tableShops = Config.databaseTablePrefix + "shops"; + this.tableLogs = Config.databaseTablePrefix + "economy_logs"; + this.tableLogouts = Config.databaseTablePrefix + "player_logouts"; + new BukkitRunnable() { @Override public void run() { disconnect(); dataSource = getDataSource(); + + String autoIncrement = Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT"; String queryCreateTableShopList = - "CREATE TABLE IF NOT EXISTS shops (" - + "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + "CREATE TABLE IF NOT EXISTS " + tableShops + " (" + + "id INTEGER PRIMARY KEY " + autoIncrement + "," + "vendor TINYTEXT NOT NULL," + "product TEXT NOT NULL," + + "amount INTEGER NOT NULL," + "world TINYTEXT NOT NULL," + "x INTEGER NOT NULL," + "y INTEGER NOT NULL," @@ -73,12 +94,19 @@ public abstract class Database { + "shoptype TINYTEXT NOT NULL)"; String queryCreateTableShopLog = - "CREATE TABLE IF NOT EXISTS `shop_log` (" - + "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + "CREATE TABLE IF NOT EXISTS " + tableLogs + " (" + + "id INTEGER PRIMARY KEY " + autoIncrement + "," + + "shop_id INTEGER NOT NULL," + "timestamp TINYTEXT NOT NULL," - + "executor TINYTEXT NOT NULL," - + "product TINYTEXT NOT NULL," - + "vendor TINYTEXT NOT NULL," + + "time LONG NOT NULL," + + "player_name TINYTEXT NOT NULL," + + "player_uuid TINYTEXT NOT NULL," + + "product_name TINYTEXT NOT NULL," + + "product TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "vendor_name TINYTEXT NOT NULL," + + "vendor_uuid TINYTEXT NOT NULL," + + "admin BIT NOT NULL," + "world TINYTEXT NOT NULL," + "x INTEGER NOT NULL," + "y INTEGER NOT NULL," @@ -87,23 +115,127 @@ public abstract class Database { + "type TINYTEXT NOT NULL)"; String queryCreateTablePlayerLogout = - "CREATE TABLE IF NOT EXISTS player_logout (" + "CREATE TABLE IF NOT EXISTS " + tableLogouts + " (" + "player VARCHAR(36) PRIMARY KEY NOT NULL," + "time LONG NOT NULL)"; - try { - // Create table "shops" - try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { + String queryCheckIfOldFormat = + Database.this instanceof SQLite ? + "SELECT name FROM sqlite_master WHERE type='table' AND name='shop_log'" : + "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='shop_log'"; + + String queryRenameTableLogouts = "ALTER TABLE player_logout RENAME TO " + tableLogouts; + String queryRenameTableLogs = "ALTER TABLE shop_log RENAME TO backup_shop_log"; // for backup + String queryRenameTableShops = "ALTER TABLE shops RENAME TO backup_shops"; // for backup + + try (Connection con = dataSource.getConnection()) { + // Check if database is in old format + try (Statement s = con.createStatement()) { + ResultSet rs = s.executeQuery(queryCheckIfOldFormat); + if (rs.next()) { + plugin.getLogger().warning("Database is using old format and will be converted."); + + try (Statement s2 = con.createStatement()) { + s.executeUpdate(queryRenameTableShops); + + // Create new shops table + try (Statement s3 = con.createStatement()) { + s3.executeUpdate(queryCreateTableShopList); + } + + // Create new log table + try (Statement s4 = con.createStatement()) { + s4.executeUpdate(queryCreateTableShopLog); + } + + // Convert shop table + try (Statement s3 = con.createStatement()) { + ResultSet rs2 = s3.executeQuery("SELECT id,product FROM backup_shops"); + while (rs2.next()) { + ItemStack is = Utils.decode(rs2.getString("product")); + int amount = is.getAmount(); + is.setAmount(1); + String product = Utils.encode(is); + + String insertQuery = "INSERT INTO " + tableShops + " SELECT id,vendor,?,?,world,x,y,z,buyprice,sellprice,shoptype FROM backup_shops WHERE id = ?"; + try (PreparedStatement ps = con.prepareStatement(insertQuery)) { + ps.setString(1, product); + ps.setInt(2, amount); + ps.setInt(3, rs2.getInt("id")); + ps.executeUpdate(); + } + } + } + + // Convert log table + try (Statement s3 = con.createStatement()) { + ResultSet rs2 = s3.executeQuery("SELECT id,timestamp,executor,product,vendor FROM shop_log"); + while (rs2.next()) { + String timestamp = rs2.getString("timestamp"); + long time = 0L; + + try { + time = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(timestamp).getTime(); + } catch (ParseException e) { + plugin.debug("Failed to parse timestamp '" + timestamp + "': Time is set to 0"); + plugin.debug(e); + } + + String player = rs2.getString("executor"); + String playerUuid = player.substring(0, 36); + String playerName = player.substring(38, player.length() - 1); + + String oldProduct = rs2.getString("product"); + String product = oldProduct.split(" x ")[1]; + int amount = Integer.valueOf(oldProduct.split(" x ")[0]); + + String vendor = rs2.getString("vendor"); + String vendorUuid = vendor.substring(0, 36); + String vendorName = vendor.substring(38).replaceAll("\\)( \\(ADMIN\\))?", ""); + boolean admin = vendor.endsWith("(ADMIN)"); + + String insertQuery = "INSERT INTO " + tableLogs + " SELECT id,-1,timestamp,?,?,?,?,'Unknown',?,?,?,?,world,x,y,z,price,type FROM shop_log WHERE id = ?"; + try (PreparedStatement ps = con.prepareStatement(insertQuery)) { + ps.setLong(1, time); + ps.setString(2, playerName); + ps.setString(3, playerUuid); + ps.setString(4, product); + ps.setInt(5, amount); + ps.setString(6, vendorName); + ps.setString(7, vendorUuid); + ps.setBoolean(8, admin); + ps.setInt(9, rs2.getInt("id")); + ps.executeUpdate(); + } + } + } + + // Rename log table + try (Statement s3 = con.createStatement()) { + s3.executeUpdate(queryRenameTableLogs); + } + + // Rename logout table + try (Statement s3 = con.createStatement()) { + s3.executeUpdate(queryRenameTableLogouts); + } + } + } + } + + + // Create shop table + try (Statement s = con.createStatement()) { s.executeUpdate(queryCreateTableShopList); } - // Create table "shop_log" - try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { + // Create log table + try (Statement s = con.createStatement()) { s.executeUpdate(queryCreateTableShopLog); } - // Create table "player_logout" - try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { + // Create logout table + try (Statement s = con.createStatement()) { s.executeUpdate(queryCreateTablePlayerLogout); } @@ -112,19 +244,19 @@ public abstract class Database { cleanUpEconomy(false); } - // Count entries in table "shops" - try (Connection con = dataSource.getConnection(); Statement s = con.createStatement();) { - ResultSet rs = s.executeQuery("SELECT id FROM shops"); + // Count shops entries in database + try (Statement s = con.createStatement();) { + ResultSet rs = s.executeQuery("SELECT COUNT(id) FROM " + tableShops); + if (rs.next()) { + int count = rs.getInt(1); + + plugin.debug("Initialized database with " + count + " entries"); - int count = 0; - while (rs.next()) { - count++; - } - - plugin.debug("Initialized database with " + count + " entries"); - - if (callback != null) { - callback.callSyncResult(count); + if (callback != null) { + callback.callSyncResult(count); + } + } else { + throw new SQLException("Count result set has no entries"); } } } catch (SQLException e) { @@ -151,7 +283,7 @@ public abstract class Database { @Override public void run() { try (Connection con = dataSource.getConnection(); - PreparedStatement ps = con.prepareStatement("DELETE FROM shops WHERE id = ?")) { + PreparedStatement ps = con.prepareStatement("DELETE FROM " + tableShops + " WHERE id = ?")) { ps.setInt(1, shop.getID()); ps.executeUpdate(); @@ -173,53 +305,14 @@ public abstract class Database { }.runTaskAsynchronously(plugin); } - /** - * @param id ID of the shop - * @param callback Callback that - if succeeded - returns whether a shop with - * the given ID exists (as {@code boolean}) - */ - public void isShop(final int id, final Callback callback) { - new BukkitRunnable() { - @Override - public void run() { - try (Connection con = dataSource.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM shops WHERE id = ?")) { - ps.setInt(1, id); - ResultSet rs = ps.executeQuery(); - - while (rs.next()) { - if (rs.getInt("id") == id) { - if (callback != null) { - callback.callSyncResult(true); - } - return; - } - } - - if (callback != null) { - callback.callSyncResult(false); - } - } catch (SQLException ex) { - if (callback != null) { - callback.callSyncError(ex); - } - - plugin.getLogger().severe("Failed to check if shop exists in the database"); - plugin.debug("Failed to check if shop with ID exists (#" + id + ")"); - plugin.debug(ex); - } - } - }.runTaskAsynchronously(plugin); - } - /** * Get all shops from the database * + * @param showConsoleMessages Whether console messages (errors or warnings) + * should be shown * @param callback Callback that - if succeeded - returns a read-only * collection of all shops (as * {@code Collection}) - * @param showConsoleMessages Whether console messages (errors or warnings) - * should be shown */ public void getShops(final boolean showConsoleMessages, final Callback> callback) { new BukkitRunnable() { @@ -228,7 +321,7 @@ public abstract class Database { ArrayList shops = new ArrayList<>(); try (Connection con = dataSource.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM shops")) { + PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableShops + "")) { ResultSet rs = ps.executeQuery(); while (rs.next()) { @@ -238,9 +331,6 @@ public abstract class Database { String worldName = rs.getString("world"); World world = Bukkit.getWorld(worldName); - int x = rs.getInt("x"); - int y = rs.getInt("y"); - int z = rs.getInt("z"); if (world == null) { WorldNotFoundException ex = new WorldNotFoundException(worldName); @@ -253,16 +343,20 @@ public abstract class Database { continue; } + int x = rs.getInt("x"); + int y = rs.getInt("y"); + int z = rs.getInt("z"); Location location = new Location(world, x, y, z); - - plugin.debug("Initializing new shop... (#" + id + ")"); - OfflinePlayer vendor = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("vendor"))); - ItemStack product = Utils.decode(rs.getString("product")); + ItemStack itemStack = Utils.decode(rs.getString("product")); + int amount = rs.getInt("amount"); + ShopProduct product = new ShopProduct(itemStack, amount); double buyPrice = rs.getDouble("buyprice"); double sellPrice = rs.getDouble("sellprice"); ShopType shopType = ShopType.valueOf(rs.getString("shoptype")); + plugin.debug("Initializing new shop... (#" + id + ")"); + shops.add(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType)); } @@ -291,8 +385,8 @@ public abstract class Database { * given (as {@code int}) */ public void addShop(final Shop shop, final Callback callback) { - final String queryNoId = "REPLACE INTO shops (vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?)"; - final String queryWithId = "REPLACE INTO shops (id,vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?)"; + final String queryNoId = "REPLACE INTO " + tableShops + " (vendor,product,amount,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?)"; + final String queryWithId = "REPLACE INTO " + tableShops + " (id,vendor,product,amount,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?,?)"; new BukkitRunnable() { @Override @@ -308,14 +402,15 @@ public abstract class Database { } ps.setString(i+1, shop.getVendor().getUniqueId().toString()); - ps.setString(i+2, Utils.encode(shop.getProduct())); - ps.setString(i+3, shop.getLocation().getWorld().getName()); - ps.setInt(i+4, shop.getLocation().getBlockX()); - ps.setInt(i+5, shop.getLocation().getBlockY()); - ps.setInt(i+6, shop.getLocation().getBlockZ()); - ps.setDouble(i+7, shop.getBuyPrice()); - ps.setDouble(i+8, shop.getSellPrice()); - ps.setString(i+9, shop.getShopType().toString()); + ps.setString(i+2, Utils.encode(shop.getProduct().getItemStack())); + ps.setInt(i+3, shop.getProduct().getAmount()); + ps.setString(i+4, shop.getLocation().getWorld().getName()); + ps.setInt(i+5, shop.getLocation().getBlockX()); + ps.setInt(i+6, shop.getLocation().getBlockY()); + ps.setInt(i+7, shop.getLocation().getBlockZ()); + ps.setDouble(i+8, shop.getBuyPrice()); + ps.setDouble(i+9, shop.getSellPrice()); + ps.setString(i+10, shop.getShopType().toString()); ps.executeUpdate(); if (!shop.hasId()) { @@ -356,8 +451,10 @@ public abstract class Database { * @param type Whether the executor bought or sold * @param callback Callback that - if succeeded - returns {@code null} */ - public void logEconomy(final Player executor, Shop shop, ItemStack product, double price, Type type, final Callback callback) { - final String query = "INSERT INTO shop_log (timestamp,executor,product,vendor,world,x,y,z,price,type) VALUES(?,?,?,?,?,?,?,?,?,?)"; + public void logEconomy(final Player executor, Shop shop, ShopProduct product, double price, Type type, final Callback callback) { + final String query = "INSERT INTO " + tableLogs + " (shop_id,timestamp,time,player_name,player_uuid,product_name,product,amount," + + "vendor_name,vendor_uuid,admin,world,x,y,z,price,type) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + if (Config.enableEconomyLog) { new BukkitRunnable() { @Override @@ -365,16 +462,25 @@ public abstract class Database { try (Connection con = dataSource.getConnection(); PreparedStatement ps = con.prepareStatement(query)) { - ps.setString(1, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime())); - ps.setString(2, executor.getUniqueId().toString() + " (" + executor.getName() + ")"); - ps.setString(3, product.getAmount() + " x " + LanguageUtils.getItemName(product)); - ps.setString(4, shop.getVendor().getUniqueId().toString() + " (" + shop.getVendor().getName() + ")" + (shop.getShopType() == ShopType.ADMIN ? " (ADMIN)" : "")); - ps.setString(5, shop.getLocation().getWorld().getName()); - ps.setInt(6, shop.getLocation().getBlockX()); - ps.setInt(7, shop.getLocation().getBlockY()); - ps.setInt(8, shop.getLocation().getBlockZ()); - ps.setDouble(9, price); - ps.setString(10, type.toString()); + long millis = System.currentTimeMillis(); + + ps.setInt(1, shop.getID()); + ps.setString(2, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(millis)); + ps.setLong(3, millis); + ps.setString(4, executor.getName()); + ps.setString(5, executor.getUniqueId().toString()); + ps.setString(6, LanguageUtils.getItemName(product.getItemStack())); + ps.setString(7, Utils.encode(product.getItemStack())); + ps.setInt(8, product.getAmount()); + ps.setString(9, shop.getVendor().getName()); + ps.setString(10, shop.getVendor().getUniqueId().toString()); + ps.setBoolean(11, shop.getShopType() == ShopType.ADMIN); + ps.setString(12, shop.getLocation().getWorld().getName()); + ps.setInt(13, shop.getLocation().getBlockX()); + ps.setInt(14, shop.getLocation().getBlockY()); + ps.setInt(15, shop.getLocation().getBlockZ()); + ps.setDouble(16, price); + ps.setString(17, type.toString()); ps.executeUpdate(); if (callback != null) { @@ -409,13 +515,9 @@ public abstract class Database { BukkitRunnable runnable = new BukkitRunnable() { @Override public void run() { - Calendar cal = Calendar.getInstance(); - long time = System.currentTimeMillis(); - cal.add(Calendar.DATE, -Config.cleanupEconomyLogDays); - time -= Config.cleanupEconomyLogDays * 86400000L; - String logPurgeLimit = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cal.getTime()); - String queryCleanUpLog = "DELETE FROM shop_log WHERE timestamp < '" + logPurgeLimit + "'"; - String queryCleanUpPlayers = "DELETE FROM player_logout WHERE time < " + String.valueOf(time); + long time = System.currentTimeMillis() - Config.cleanupEconomyLogDays * 86400000L; + String queryCleanUpLog = "DELETE FROM " + tableLogs + " WHERE time < " + time; + String queryCleanUpPlayers = "DELETE FROM " + tableLogouts + " WHERE time < " + time; try (Connection con = dataSource.getConnection(); Statement s = con.createStatement(); @@ -452,36 +554,24 @@ public abstract class Database { new BukkitRunnable() { @Override public void run() { - String vendor = String.format("%s (%s)", player.getUniqueId().toString(), player.getName()); double revenue = 0; try (Connection con = dataSource.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM shop_log WHERE vendor = ?")) { - ps.setString(1, vendor); + PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableLogs + " WHERE vendor_uuid = ?")) { + ps.setString(1, player.getUniqueId().toString()); ResultSet rs = ps.executeQuery(); while (rs.next()) { - if (rs.getString("vendor").equals(vendor)) { - double singleRevenue = rs.getDouble("price"); - ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type")); + long timestamp = rs.getLong("time"); + double singleRevenue = rs.getDouble("price"); + ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type")); - if (type == ShopBuySellEvent.Type.SELL) { - singleRevenue = -singleRevenue; - } + if (type == ShopBuySellEvent.Type.SELL) { + singleRevenue = -singleRevenue; + } - long timestamp; - - try { - timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(rs.getString("timestamp")).getTime(); - } catch (ParseException ex) { - plugin.debug("Failed to get revenue from player \"" + player.getUniqueId().toString() + "\""); - plugin.debug(ex); - continue; - } - - if (timestamp > logoutTime) { - revenue += singleRevenue; - } + if (timestamp > logoutTime) { + revenue += singleRevenue; } } @@ -505,18 +595,16 @@ public abstract class Database { * Log a logout to the database * * @param player Player who logged out - * @param timestamp Time in milliseconds when the player logged out * @param callback Callback that - if succeeded - returns {@code null} */ - public void logLogout(final Player player, final long timestamp, final Callback callback) { + public void logLogout(final Player player, final Callback callback) { new BukkitRunnable() { @Override public void run() { - try (Connection con = dataSource.getConnection(); - PreparedStatement ps = con.prepareStatement("REPLACE INTO player_logout (player,time) VALUES(?,?)")) { + PreparedStatement ps = con.prepareStatement("REPLACE INTO " + tableLogouts + " (player,time) VALUES(?,?)")) { ps.setString(1, player.getUniqueId().toString()); - ps.setLong(2, timestamp); + ps.setLong(2, System.currentTimeMillis()); ps.executeUpdate(); if (callback != null) { @@ -549,19 +637,14 @@ public abstract class Database { new BukkitRunnable() { @Override public void run() { - String uuid = player.getUniqueId().toString(); - try (Connection con = dataSource.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM player_logout WHERE player = ?")) { - ps.setString(1, uuid); + PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableLogouts + " WHERE player = ?")) { + ps.setString(1, player.getUniqueId().toString()); ResultSet rs = ps.executeQuery(); - while (rs.next()) { - if (rs.getString("player").equals(uuid)) { - if (callback != null) { - callback.callSyncResult(rs.getLong("time")); - } - return; + if (rs.next()) { + if (callback != null) { + callback.callSyncResult(rs.getLong("time")); } } diff --git a/src/main/java/de/epiceric/shopchest/utils/ClickType.java b/src/main/java/de/epiceric/shopchest/utils/ClickType.java index 7562ce1..dbf7765 100644 --- a/src/main/java/de/epiceric/shopchest/utils/ClickType.java +++ b/src/main/java/de/epiceric/shopchest/utils/ClickType.java @@ -1,9 +1,9 @@ package de.epiceric.shopchest.utils; import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.shop.ShopProduct; import de.epiceric.shopchest.shop.Shop.ShopType; import org.bukkit.OfflinePlayer; -import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; @@ -18,7 +18,7 @@ public class ClickType { private static Map playerTimers = new HashMap<>(); private EnumClickType enumClickType; - private ItemStack product; + private ShopProduct product; private double buyPrice; private double sellPrice; private ShopType shopType; @@ -27,7 +27,7 @@ public class ClickType { this.enumClickType = enumClickType; } - public ClickType(EnumClickType enumClickType, ItemStack product, double buyPrice, double sellPrice, ShopType shopType) { + public ClickType(EnumClickType enumClickType, ShopProduct product, double buyPrice, double sellPrice, ShopType shopType) { this.enumClickType = enumClickType; this.product = product; this.sellPrice = sellPrice; @@ -90,7 +90,7 @@ public class ClickType { /** * @return If {@link #getClickType()} returns {@link EnumClickType#CREATE}, this returns the item, the player has hold in his hands, else null. */ - public ItemStack getProduct() { + public ShopProduct getProduct() { return product; } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 2502735..49c854b 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -251,6 +251,9 @@ database: # Either use 'SQLite' or 'MySQL'. Otherwise you will break the plugin! type: "SQLite" + # Set the prefix of all table names related to this plugin. + table-prefix: "shopchest_" + # If the specified type is 'MySQL', here you configure the... # (You can leave this empty if you're using SQLite) mysql: