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
This commit is contained in:
Eric 2018-11-10 20:34:11 +01:00
parent 4c6c87dc08
commit 6ae12f65c0
9 changed files with 342 additions and 215 deletions

View File

@ -13,6 +13,7 @@ import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message; import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement; import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.ShopProduct;
import de.epiceric.shopchest.utils.Callback; import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.ClickType; import de.epiceric.shopchest.utils.ClickType;
import de.epiceric.shopchest.utils.ItemUtils; import de.epiceric.shopchest.utils.ItemUtils;
@ -354,11 +355,8 @@ class ShopCommandExecutor implements CommandExecutor {
} }
} }
ItemStack product = new ItemStack(inHand.getType(), amount, inHand.getDurability()); if (Enchantment.DURABILITY.canEnchantItem(inHand)) {
product.setItemMeta(inHand.getItemMeta()); if (inHand.getDurability() > 0 && !Config.allowBrokenItems) {
if (Enchantment.DURABILITY.canEnchantItem(product)) {
if (product.getDurability() > 0 && !Config.allowBrokenItems) {
p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_BROKEN_ITEM)); p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_BROKEN_ITEM));
plugin.debug(p.getName() + "'s item is broken"); plugin.debug(p.getName() + "'s item is broken");
return; 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)); ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, product, null, buyPrice, sellPrice, shopType));
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);

View File

@ -87,7 +87,12 @@ public class Config {
public static String databaseMySqlPassword; 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; public static Database.DatabaseType databaseType;
@ -474,6 +479,7 @@ public class Config {
databaseMySqlDatabase = plugin.getConfig().getString("database.mysql.database"); databaseMySqlDatabase = plugin.getConfig().getString("database.mysql.database");
databaseMySqlUsername = plugin.getConfig().getString("database.mysql.username"); databaseMySqlUsername = plugin.getConfig().getString("database.mysql.username");
databaseMySqlPassword = plugin.getConfig().getString("database.mysql.password"); databaseMySqlPassword = plugin.getConfig().getString("database.mysql.password");
databaseTablePrefix = plugin.getConfig().getString("database.table-prefix");
databaseType = Database.DatabaseType.valueOf(plugin.getConfig().getString("database.type")); databaseType = Database.DatabaseType.valueOf(plugin.getConfig().getString("database.type"));
minimumPrices = (plugin.getConfig().getConfigurationSection("minimum-prices") == null) ? new HashSet<String>() : plugin.getConfig().getConfigurationSection("minimum-prices").getKeys(true); minimumPrices = (plugin.getConfig().getConfigurationSection("minimum-prices") == null) ? new HashSet<String>() : plugin.getConfig().getConfigurationSection("minimum-prices").getKeys(true);
maximumPrices = (plugin.getConfig().getConfigurationSection("maximum-prices") == null) ? new HashSet<String>() : plugin.getConfig().getConfigurationSection("maximum-prices").getKeys(true); maximumPrices = (plugin.getConfig().getConfigurationSection("maximum-prices") == null) ? new HashSet<String>() : plugin.getConfig().getConfigurationSection("maximum-prices").getKeys(true);

View File

@ -57,8 +57,7 @@ public class NotifyPlayerOnJoinListener implements Listener {
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent e) { public void onPlayerQuit(PlayerQuitEvent e) {
long time = System.currentTimeMillis(); plugin.getShopDatabase().logLogout(e.getPlayer(), null);
plugin.getShopDatabase().logLogout(e.getPlayer(), time, null);
} }
} }

View File

@ -24,6 +24,7 @@ import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.nms.Hologram; import de.epiceric.shopchest.nms.Hologram;
import de.epiceric.shopchest.nms.JsonBuilder; import de.epiceric.shopchest.nms.JsonBuilder;
import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.ShopProduct;
import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.sql.Database; import de.epiceric.shopchest.sql.Database;
import de.epiceric.shopchest.utils.ClickType; import de.epiceric.shopchest.utils.ClickType;
@ -301,7 +302,7 @@ public class ShopInteractListener implements Listener {
if (b.getRelative(BlockFace.UP).getType() == Material.AIR) { if (b.getRelative(BlockFace.UP).getType() == Material.AIR) {
ClickType clickType = ClickType.getPlayerClickType(p); ClickType clickType = ClickType.getPlayerClickType(p);
ItemStack product = clickType.getProduct(); ShopProduct product = clickType.getProduct();
double buyPrice = clickType.getBuyPrice(); double buyPrice = clickType.getBuyPrice();
double sellPrice = clickType.getSellPrice(); double sellPrice = clickType.getSellPrice();
ShopType shopType = clickType.getShopType(); ShopType shopType = clickType.getShopType();
@ -484,9 +485,10 @@ public class ShopInteractListener implements Listener {
} else { } else {
if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) { if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) {
Chest c = (Chest) b.getState(); 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) { if (confirmed || !Config.confirmShopping) {
buy(p, shop, p.isSneaking()); buy(p, shop, p.isSneaking());
if (Config.confirmShopping) { if (Config.confirmShopping) {
@ -503,7 +505,7 @@ public class ShopInteractListener implements Listener {
needsConfirmation.put(p.getUniqueId(), ids); needsConfirmation.put(p.getUniqueId(), ids);
} }
} else { } else {
if (Config.autoCalculateItemAmount && Utils.getAmount(c.getInventory(), shop.getProduct()) > 0) { if (Config.autoCalculateItemAmount && Utils.getAmount(c.getInventory(), itemStack) > 0) {
if (confirmed || !Config.confirmShopping) { if (confirmed || !Config.confirmShopping) {
buy(p, shop, p.isSneaking()); buy(p, shop, p.isSneaking());
if (Config.confirmShopping) { if (Config.confirmShopping) {
@ -524,7 +526,7 @@ public class ShopInteractListener implements Listener {
if (shop.getVendor().isOnline() && Config.enableVendorMessages) { if (shop.getVendor().isOnline() && Config.enableVendorMessages) {
shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.VENDOR_OUT_OF_STOCK, shop.getVendor().getPlayer().sendMessage(LanguageUtils.getMessage(Message.VENDOR_OUT_OF_STOCK,
new Replacement(Placeholder.AMOUNT, String.valueOf(shop.getProduct().getAmount())), 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"); 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); externalPluginsAllowed = WorldGuardWrapper.getInstance().queryStateFlag(p, b.getLocation(), flagName).orElse(false);
} }
ItemStack itemStack = shop.getProduct().getItemStack();
if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) { if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) {
boolean stack = p.isSneaking() && !Utils.hasAxeInHand(p); 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) { if (confirmed || !Config.confirmShopping) {
sell(p, shop, stack); sell(p, shop, stack);
if (Config.confirmShopping) { if (Config.confirmShopping) {
@ -590,7 +594,7 @@ public class ShopInteractListener implements Listener {
needsConfirmation.put(p.getUniqueId(), ids); needsConfirmation.put(p.getUniqueId(), ids);
} }
} else { } else {
if (Config.autoCalculateItemAmount && Utils.getAmount(p.getInventory(), shop.getProduct()) > 0) { if (Config.autoCalculateItemAmount && Utils.getAmount(p.getInventory(), itemStack) > 0) {
if (confirmed || !Config.confirmShopping) { if (confirmed || !Config.confirmShopping) {
sell(p, shop, stack); sell(p, shop, stack);
if (Config.confirmShopping) { if (Config.confirmShopping) {
@ -716,7 +720,7 @@ public class ShopInteractListener implements Listener {
* @param sellPrice Sell price * @param sellPrice Sell price
* @param shopType Type of the shop * @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..."); plugin.debug(executor.getName() + " is creating new shop...");
if (!executor.hasPermission(Permissions.CREATE)) { if (!executor.hasPermission(Permissions.CREATE)) {
@ -825,7 +829,8 @@ public class ShopInteractListener implements Listener {
} }
Chest c = (Chest) shop.getLocation().getBlock().getState(); 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 ? String vendorName = (shop.getVendor().getName() == null ?
shop.getVendor().getUniqueId().toString() : shop.getVendor().getName()); shop.getVendor().getUniqueId().toString() : shop.getVendor().getName());
@ -863,21 +868,21 @@ public class ShopInteractListener implements Listener {
* @param product The product of the shop * @param product The product of the shop
* @return A {@link JsonBuilder} that can send the message via {@link JsonBuilder#sendJson(Player)} * @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 // 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 // the item name after splitting at Placeholder.ITEM_NAME
String productString = " " + LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT, String productString = " " + LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT,
new Replacement(Placeholder.AMOUNT, String.valueOf(product.getAmount()))) + " "; new Replacement(Placeholder.AMOUNT, String.valueOf(product.getAmount()))) + " ";
String[] parts = productString.split(Placeholder.ITEM_NAME.toString()); String[] parts = productString.split(Placeholder.ITEM_NAME.toString());
String productName = LanguageUtils.getItemName(product); String productName = LanguageUtils.getItemName(product.getItemStack());
String jsonItem = ""; String jsonItem = "";
JsonBuilder jb = new JsonBuilder(plugin); JsonBuilder jb = new JsonBuilder(plugin);
JsonBuilder.PartArray rootArray = new JsonBuilder.PartArray(); JsonBuilder.PartArray rootArray = new JsonBuilder.PartArray();
try { try {
Class<?> craftItemStackClass = Utils.getCraftClass("inventory.CraftItemStack"); 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"); Class<?> nbtTagCompoundClass = Utils.getNMSClass("NBTTagCompound");
Object nbtTagCompound = nbtTagCompoundClass.getConstructor().newInstance(); Object nbtTagCompound = nbtTagCompoundClass.getConstructor().newInstance();
nmsStack.getClass().getMethod("save", nbtTagCompoundClass).invoke(nmsStack, nbtTagCompound); 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) { private void buy(Player executor, final Shop shop, boolean stack) {
plugin.debug(executor.getName() + " is buying (#" + shop.getID() + ")"); plugin.debug(executor.getName() + " is buying (#" + shop.getID() + ")");
ItemStack itemStack = shop.getProduct().getItemStack();
int amount = shop.getProduct().getAmount(); int amount = shop.getProduct().getAmount();
if (stack) amount = shop.getProduct().getMaxStackSize(); if (stack) amount = itemStack.getMaxStackSize();
String worldName = shop.getLocation().getWorld().getName(); String worldName = shop.getLocation().getWorld().getName();
@ -966,14 +972,14 @@ public class ShopInteractListener implements Listener {
Block b = shop.getLocation().getBlock(); Block b = shop.getLocation().getBlock();
Chest c = (Chest) b.getState(); 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) { if (amountForChestItems == 0 && shop.getShopType() != ShopType.ADMIN) {
executor.sendMessage(LanguageUtils.getMessage(Message.OUT_OF_STOCK)); executor.sendMessage(LanguageUtils.getMessage(Message.OUT_OF_STOCK));
return; return;
} }
ItemStack product = new ItemStack(shop.getProduct()); ItemStack product = new ItemStack(itemStack);
if (stack) product.setAmount(amount); if (stack) product.setAmount(amount);
Inventory inventory = executor.getInventory(); Inventory inventory = executor.getInventory();
@ -996,14 +1002,12 @@ public class ShopInteractListener implements Listener {
if (newAmount > amount) newAmount = amount; if (newAmount > amount) newAmount = amount;
ShopProduct newProduct = new ShopProduct(product, newAmount);
double newPrice = (price / amount) * newAmount; double newPrice = (price / amount) * newAmount;
if (freeSpace >= newAmount) { if (freeSpace >= newAmount) {
plugin.debug(executor.getName() + " has enough inventory space for " + freeSpace + " items (#" + shop.getID() + ")"); 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); EconomyResponse r = econ.withdrawPlayer(executor, worldName, newPrice);
if (r.transactionSuccess()) { if (r.transactionSuccess()) {
@ -1105,8 +1109,9 @@ public class ShopInteractListener implements Listener {
private void sell(Player executor, final Shop shop, boolean stack) { private void sell(Player executor, final Shop shop, boolean stack) {
plugin.debug(executor.getName() + " is selling (#" + shop.getID() + ")"); plugin.debug(executor.getName() + " is selling (#" + shop.getID() + ")");
ItemStack itemStack = shop.getProduct().getItemStack();
int amount = shop.getProduct().getAmount(); int amount = shop.getProduct().getAmount();
if (stack) amount = shop.getProduct().getMaxStackSize(); if (stack) amount = itemStack.getMaxStackSize();
double price = shop.getSellPrice(); double price = shop.getSellPrice();
if (stack) price = (price / shop.getProduct().getAmount()) * amount; if (stack) price = (price / shop.getProduct().getAmount()) * amount;
@ -1130,14 +1135,14 @@ public class ShopInteractListener implements Listener {
Block block = shop.getLocation().getBlock(); Block block = shop.getLocation().getBlock();
Chest chest = (Chest) block.getState(); Chest chest = (Chest) block.getState();
int amountForItemCount = Utils.getAmount(executor.getInventory(), shop.getProduct()); int amountForItemCount = Utils.getAmount(executor.getInventory(), itemStack);
if (amountForItemCount == 0) { if (amountForItemCount == 0) {
executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_ITEMS)); executor.sendMessage(LanguageUtils.getMessage(Message.NOT_ENOUGH_ITEMS));
return; return;
} }
ItemStack product = new ItemStack(shop.getProduct()); ItemStack product = new ItemStack(itemStack);
if (stack) product.setAmount(amount); if (stack) product.setAmount(amount);
Inventory inventory = chest.getInventory(); Inventory inventory = chest.getInventory();
@ -1160,14 +1165,12 @@ public class ShopInteractListener implements Listener {
if (newAmount > amount) newAmount = amount; if (newAmount > amount) newAmount = amount;
ShopProduct newProduct = new ShopProduct(product, newAmount);
double newPrice = (price / amount) * newAmount; double newPrice = (price / amount) * newAmount;
if (freeSpace >= newAmount || shop.getShopType() == ShopType.ADMIN) { if (freeSpace >= newAmount || shop.getShopType() == ShopType.ADMIN) {
plugin.debug("Chest has enough inventory space for " + freeSpace + " items (#" + shop.getID() + ")"); 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); EconomyResponse r = econ.depositPlayer(executor, worldName, newPrice);
if (r.transactionSuccess()) { if (r.transactionSuccess()) {
@ -1271,11 +1274,12 @@ public class ShopInteractListener implements Listener {
* @param itemStack Items to add * @param itemStack Items to add
* @return Whether all items were added to the inventory * @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..."); plugin.debug("Adding items to inventory...");
HashMap<Integer, ItemStack> inventoryItems = new HashMap<>(); HashMap<Integer, ItemStack> inventoryItems = new HashMap<>();
int amount = itemStack.getAmount(); ItemStack itemStack = product.getItemStack();
int amount = product.getAmount();
int added = 0; int added = 0;
if (inventory instanceof PlayerInventory) { if (inventory instanceof PlayerInventory) {
@ -1329,11 +1333,12 @@ public class ShopInteractListener implements Listener {
* @param itemStack Items to remove * @param itemStack Items to remove
* @return Whether all items were removed from the inventory * @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..."); plugin.debug("Removing items from inventory...");
HashMap<Integer, ItemStack> inventoryItems = new HashMap<>(); HashMap<Integer, ItemStack> inventoryItems = new HashMap<>();
int amount = itemStack.getAmount(); ItemStack itemStack = product.getItemStack();
int amount = product.getAmount();
int removed = 0; int removed = 0;
if (inventory instanceof PlayerInventory) { if (inventory instanceof PlayerInventory) {

View File

@ -48,7 +48,7 @@ public class Shop {
private final ShopChest plugin; private final ShopChest plugin;
private final OfflinePlayer vendor; private final OfflinePlayer vendor;
private final ItemStack product; private final ShopProduct product;
private final Location location; private final Location location;
private final double buyPrice; private final double buyPrice;
private final double sellPrice; private final double sellPrice;
@ -60,7 +60,7 @@ public class Shop {
private Location holoLocation; private Location holoLocation;
private ShopItem item; 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.id = id;
this.plugin = plugin; this.plugin = plugin;
this.vendor = vendor; this.vendor = vendor;
@ -71,7 +71,7 @@ public class Shop {
this.shopType = shopType; 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); this(-1, plugin, vendor, product, location, buyPrice, sellPrice, shopType);
} }
@ -179,13 +179,9 @@ public class Shop {
plugin.debug("Creating item (#" + id + ")"); plugin.debug("Creating item (#" + id + ")");
Location itemLocation; Location itemLocation;
ItemStack itemStack;
itemLocation = new Location(location.getWorld(), holoLocation.getX(), location.getY() + 0.9, holoLocation.getZ()); itemLocation = new Location(location.getWorld(), holoLocation.getX(), location.getY() + 0.9, holoLocation.getZ());
itemStack = product.clone(); item = new ShopItem(plugin, product.getItemStack(), itemLocation);
itemStack.setAmount(1);
item = new ShopItem(plugin, itemStack, itemLocation);
} }
} }
@ -260,39 +256,41 @@ public class Shop {
private String[] getHologramText(Inventory inventory) { private String[] getHologramText(Inventory inventory) {
List<String> lines = new ArrayList<>(); List<String> lines = new ArrayList<>();
ItemStack itemStack = getProduct().getItemStack();
Map<HologramFormat.Requirement, Object> requirements = new EnumMap<>(HologramFormat.Requirement.class); Map<HologramFormat.Requirement, Object> requirements = new EnumMap<>(HologramFormat.Requirement.class);
requirements.put(HologramFormat.Requirement.VENDOR, getVendor().getName()); requirements.put(HologramFormat.Requirement.VENDOR, getVendor().getName());
requirements.put(HologramFormat.Requirement.AMOUNT, getProduct().getAmount()); 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_TYPE, itemStack.getType() + (itemStack.getDurability() > 0 ? ":" + itemStack.getDurability() : ""));
requirements.put(HologramFormat.Requirement.ITEM_NAME, getProduct().hasItemMeta() ? getProduct().getItemMeta().getDisplayName() : null); requirements.put(HologramFormat.Requirement.ITEM_NAME, itemStack.hasItemMeta() ? itemStack.getItemMeta().getDisplayName() : null);
requirements.put(HologramFormat.Requirement.HAS_ENCHANTMENT, !LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(getProduct())).isEmpty()); requirements.put(HologramFormat.Requirement.HAS_ENCHANTMENT, !LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(itemStack)).isEmpty());
requirements.put(HologramFormat.Requirement.BUY_PRICE, getBuyPrice()); requirements.put(HologramFormat.Requirement.BUY_PRICE, getBuyPrice());
requirements.put(HologramFormat.Requirement.SELL_PRICE, getSellPrice()); requirements.put(HologramFormat.Requirement.SELL_PRICE, getSellPrice());
requirements.put(HologramFormat.Requirement.HAS_POTION_EFFECT, ItemUtils.getPotionEffect(getProduct()) != null); requirements.put(HologramFormat.Requirement.HAS_POTION_EFFECT, ItemUtils.getPotionEffect(itemStack) != null);
requirements.put(HologramFormat.Requirement.IS_MUSIC_DISC, getProduct().getType().isRecord()); requirements.put(HologramFormat.Requirement.IS_MUSIC_DISC, itemStack.getType().isRecord());
requirements.put(HologramFormat.Requirement.IS_POTION_EXTENDED, ItemUtils.isExtendedPotion(getProduct())); requirements.put(HologramFormat.Requirement.IS_POTION_EXTENDED, ItemUtils.isExtendedPotion(itemStack));
requirements.put(HologramFormat.Requirement.IS_WRITTEN_BOOK, getProduct().getType() == Material.WRITTEN_BOOK); 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.ADMIN_SHOP, getShopType() == ShopType.ADMIN);
requirements.put(HologramFormat.Requirement.NORMAL_SHOP, getShopType() == ShopType.NORMAL); requirements.put(HologramFormat.Requirement.NORMAL_SHOP, getShopType() == ShopType.NORMAL);
requirements.put(HologramFormat.Requirement.IN_STOCK, Utils.getAmount(inventory, getProduct())); requirements.put(HologramFormat.Requirement.IN_STOCK, Utils.getAmount(inventory, itemStack));
requirements.put(HologramFormat.Requirement.MAX_STACK, getProduct().getMaxStackSize()); requirements.put(HologramFormat.Requirement.MAX_STACK, itemStack.getMaxStackSize());
requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, getProduct())); requirements.put(HologramFormat.Requirement.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, itemStack));
requirements.put(HologramFormat.Requirement.DURABILITY, getProduct().getDurability()); requirements.put(HologramFormat.Requirement.DURABILITY, itemStack.getDurability());
Map<Placeholder, Object> placeholders = new EnumMap<>(Placeholder.class); Map<Placeholder, Object> placeholders = new EnumMap<>(Placeholder.class);
placeholders.put(Placeholder.VENDOR, getVendor().getName()); placeholders.put(Placeholder.VENDOR, getVendor().getName());
placeholders.put(Placeholder.AMOUNT, getProduct().getAmount()); placeholders.put(Placeholder.AMOUNT, getProduct().getAmount());
placeholders.put(Placeholder.ITEM_NAME, LanguageUtils.getItemName(getProduct())); placeholders.put(Placeholder.ITEM_NAME, LanguageUtils.getItemName(itemStack));
placeholders.put(Placeholder.ENCHANTMENT, LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(getProduct()))); placeholders.put(Placeholder.ENCHANTMENT, LanguageUtils.getEnchantmentString(ItemUtils.getEnchantments(itemStack)));
placeholders.put(Placeholder.BUY_PRICE, getBuyPrice()); placeholders.put(Placeholder.BUY_PRICE, getBuyPrice());
placeholders.put(Placeholder.SELL_PRICE, getSellPrice()); placeholders.put(Placeholder.SELL_PRICE, getSellPrice());
placeholders.put(Placeholder.POTION_EFFECT, LanguageUtils.getPotionEffectName(getProduct())); placeholders.put(Placeholder.POTION_EFFECT, LanguageUtils.getPotionEffectName(itemStack));
placeholders.put(Placeholder.MUSIC_TITLE, LanguageUtils.getMusicDiscName(getProduct().getType())); placeholders.put(Placeholder.MUSIC_TITLE, LanguageUtils.getMusicDiscName(itemStack.getType()));
placeholders.put(Placeholder.GENERATION, LanguageUtils.getBookGenerationName(getProduct())); placeholders.put(Placeholder.GENERATION, LanguageUtils.getBookGenerationName(itemStack));
placeholders.put(Placeholder.STOCK, Utils.getAmount(inventory, getProduct())); placeholders.put(Placeholder.STOCK, Utils.getAmount(inventory, itemStack));
placeholders.put(Placeholder.MAX_STACK, getProduct().getMaxStackSize()); placeholders.put(Placeholder.MAX_STACK, itemStack.getMaxStackSize());
placeholders.put(Placeholder.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, getProduct())); placeholders.put(Placeholder.CHEST_SPACE, Utils.getFreeSpaceForItem(inventory, itemStack));
placeholders.put(Placeholder.DURABILITY, getProduct().getDurability()); placeholders.put(Placeholder.DURABILITY, itemStack.getDurability());
int lineCount = plugin.getHologramFormat().getLineCount(); int lineCount = plugin.getHologramFormat().getLineCount();
@ -407,7 +405,7 @@ public class Shop {
/** /**
* @return Product the shop sells (or buys) * @return Product the shop sells (or buys)
*/ */
public ItemStack getProduct() { public ShopProduct getProduct() {
return product; return product;
} }

View File

@ -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;
}
}

View File

@ -7,6 +7,7 @@ import de.epiceric.shopchest.event.ShopBuySellEvent.Type;
import de.epiceric.shopchest.exceptions.WorldNotFoundException; import de.epiceric.shopchest.exceptions.WorldNotFoundException;
import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.ShopProduct;
import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.utils.Callback; import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.Utils; import de.epiceric.shopchest.utils.Utils;
@ -18,11 +19,14 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable; 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.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -35,6 +39,10 @@ public abstract class Database {
private static Set<String> notFoundWorlds = new HashSet<>(); private static Set<String> notFoundWorlds = new HashSet<>();
private String tableShops;
private String tableLogs;
private String tableLogouts;
ShopChest plugin; ShopChest plugin;
HikariDataSource dataSource; HikariDataSource dataSource;
@ -52,6 +60,16 @@ public abstract class Database {
* that were found (as {@code int}) * that were found (as {@code int})
*/ */
public void connect(final Callback<Integer> callback) { public void connect(final Callback<Integer> 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() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
@ -59,11 +77,14 @@ public abstract class Database {
dataSource = getDataSource(); dataSource = getDataSource();
String autoIncrement = Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT";
String queryCreateTableShopList = String queryCreateTableShopList =
"CREATE TABLE IF NOT EXISTS shops (" "CREATE TABLE IF NOT EXISTS " + tableShops + " ("
+ "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + "id INTEGER PRIMARY KEY " + autoIncrement + ","
+ "vendor TINYTEXT NOT NULL," + "vendor TINYTEXT NOT NULL,"
+ "product TEXT NOT NULL," + "product TEXT NOT NULL,"
+ "amount INTEGER NOT NULL,"
+ "world TINYTEXT NOT NULL," + "world TINYTEXT NOT NULL,"
+ "x INTEGER NOT NULL," + "x INTEGER NOT NULL,"
+ "y INTEGER NOT NULL," + "y INTEGER NOT NULL,"
@ -73,12 +94,19 @@ public abstract class Database {
+ "shoptype TINYTEXT NOT NULL)"; + "shoptype TINYTEXT NOT NULL)";
String queryCreateTableShopLog = String queryCreateTableShopLog =
"CREATE TABLE IF NOT EXISTS `shop_log` (" "CREATE TABLE IF NOT EXISTS " + tableLogs + " ("
+ "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + "id INTEGER PRIMARY KEY " + autoIncrement + ","
+ "shop_id INTEGER NOT NULL,"
+ "timestamp TINYTEXT NOT NULL," + "timestamp TINYTEXT NOT NULL,"
+ "executor TINYTEXT NOT NULL," + "time LONG NOT NULL,"
+ "product TINYTEXT NOT NULL," + "player_name TINYTEXT NOT NULL,"
+ "vendor 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," + "world TINYTEXT NOT NULL,"
+ "x INTEGER NOT NULL," + "x INTEGER NOT NULL,"
+ "y INTEGER NOT NULL," + "y INTEGER NOT NULL,"
@ -87,23 +115,127 @@ public abstract class Database {
+ "type TINYTEXT NOT NULL)"; + "type TINYTEXT NOT NULL)";
String queryCreateTablePlayerLogout = String queryCreateTablePlayerLogout =
"CREATE TABLE IF NOT EXISTS player_logout (" "CREATE TABLE IF NOT EXISTS " + tableLogouts + " ("
+ "player VARCHAR(36) PRIMARY KEY NOT NULL," + "player VARCHAR(36) PRIMARY KEY NOT NULL,"
+ "time LONG NOT NULL)"; + "time LONG NOT NULL)";
try { String queryCheckIfOldFormat =
// Create table "shops" Database.this instanceof SQLite ?
try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { "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); s.executeUpdate(queryCreateTableShopList);
} }
// Create table "shop_log" // Create log table
try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { try (Statement s = con.createStatement()) {
s.executeUpdate(queryCreateTableShopLog); s.executeUpdate(queryCreateTableShopLog);
} }
// Create table "player_logout" // Create logout table
try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { try (Statement s = con.createStatement()) {
s.executeUpdate(queryCreateTablePlayerLogout); s.executeUpdate(queryCreateTablePlayerLogout);
} }
@ -112,19 +244,19 @@ public abstract class Database {
cleanUpEconomy(false); cleanUpEconomy(false);
} }
// Count entries in table "shops" // Count shops entries in database
try (Connection con = dataSource.getConnection(); Statement s = con.createStatement();) { try (Statement s = con.createStatement();) {
ResultSet rs = s.executeQuery("SELECT id FROM shops"); ResultSet rs = s.executeQuery("SELECT COUNT(id) FROM " + tableShops);
if (rs.next()) {
int count = rs.getInt(1);
int count = 0; plugin.debug("Initialized database with " + count + " entries");
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) { } catch (SQLException e) {
@ -151,7 +283,7 @@ public abstract class Database {
@Override @Override
public void run() { public void run() {
try (Connection con = dataSource.getConnection(); 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.setInt(1, shop.getID());
ps.executeUpdate(); ps.executeUpdate();
@ -173,53 +305,14 @@ public abstract class Database {
}.runTaskAsynchronously(plugin); }.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<Boolean> 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 * 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 * @param callback Callback that - if succeeded - returns a read-only
* collection of all shops (as * collection of all shops (as
* {@code Collection<Shop>}) * {@code Collection<Shop>})
* @param showConsoleMessages Whether console messages (errors or warnings)
* should be shown
*/ */
public void getShops(final boolean showConsoleMessages, final Callback<Collection<Shop>> callback) { public void getShops(final boolean showConsoleMessages, final Callback<Collection<Shop>> callback) {
new BukkitRunnable() { new BukkitRunnable() {
@ -228,7 +321,7 @@ public abstract class Database {
ArrayList<Shop> shops = new ArrayList<>(); ArrayList<Shop> shops = new ArrayList<>();
try (Connection con = dataSource.getConnection(); try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM shops")) { PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableShops + "")) {
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
@ -238,9 +331,6 @@ public abstract class Database {
String worldName = rs.getString("world"); String worldName = rs.getString("world");
World world = Bukkit.getWorld(worldName); World world = Bukkit.getWorld(worldName);
int x = rs.getInt("x");
int y = rs.getInt("y");
int z = rs.getInt("z");
if (world == null) { if (world == null) {
WorldNotFoundException ex = new WorldNotFoundException(worldName); WorldNotFoundException ex = new WorldNotFoundException(worldName);
@ -253,16 +343,20 @@ public abstract class Database {
continue; continue;
} }
int x = rs.getInt("x");
int y = rs.getInt("y");
int z = rs.getInt("z");
Location location = new Location(world, x, y, z); Location location = new Location(world, x, y, z);
plugin.debug("Initializing new shop... (#" + id + ")");
OfflinePlayer vendor = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("vendor"))); 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 buyPrice = rs.getDouble("buyprice");
double sellPrice = rs.getDouble("sellprice"); double sellPrice = rs.getDouble("sellprice");
ShopType shopType = ShopType.valueOf(rs.getString("shoptype")); 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)); shops.add(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType));
} }
@ -291,8 +385,8 @@ public abstract class Database {
* given (as {@code int}) * given (as {@code int})
*/ */
public void addShop(final Shop shop, final Callback<Integer> callback) { public void addShop(final Shop shop, final Callback<Integer> callback) {
final String queryNoId = "REPLACE INTO shops (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 shops (id,vendor,product,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() { new BukkitRunnable() {
@Override @Override
@ -308,14 +402,15 @@ public abstract class Database {
} }
ps.setString(i+1, shop.getVendor().getUniqueId().toString()); ps.setString(i+1, shop.getVendor().getUniqueId().toString());
ps.setString(i+2, Utils.encode(shop.getProduct())); ps.setString(i+2, Utils.encode(shop.getProduct().getItemStack()));
ps.setString(i+3, shop.getLocation().getWorld().getName()); ps.setInt(i+3, shop.getProduct().getAmount());
ps.setInt(i+4, shop.getLocation().getBlockX()); ps.setString(i+4, shop.getLocation().getWorld().getName());
ps.setInt(i+5, shop.getLocation().getBlockY()); ps.setInt(i+5, shop.getLocation().getBlockX());
ps.setInt(i+6, shop.getLocation().getBlockZ()); ps.setInt(i+6, shop.getLocation().getBlockY());
ps.setDouble(i+7, shop.getBuyPrice()); ps.setInt(i+7, shop.getLocation().getBlockZ());
ps.setDouble(i+8, shop.getSellPrice()); ps.setDouble(i+8, shop.getBuyPrice());
ps.setString(i+9, shop.getShopType().toString()); ps.setDouble(i+9, shop.getSellPrice());
ps.setString(i+10, shop.getShopType().toString());
ps.executeUpdate(); ps.executeUpdate();
if (!shop.hasId()) { if (!shop.hasId()) {
@ -356,8 +451,10 @@ public abstract class Database {
* @param type Whether the executor bought or sold * @param type Whether the executor bought or sold
* @param callback Callback that - if succeeded - returns {@code null} * @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<Void> callback) { public void logEconomy(final Player executor, Shop shop, ShopProduct product, double price, Type type, final Callback<Void> callback) {
final String query = "INSERT INTO shop_log (timestamp,executor,product,vendor,world,x,y,z,price,type) VALUES(?,?,?,?,?,?,?,?,?,?)"; 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) { if (Config.enableEconomyLog) {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
@ -365,16 +462,25 @@ public abstract class Database {
try (Connection con = dataSource.getConnection(); try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(query)) { PreparedStatement ps = con.prepareStatement(query)) {
ps.setString(1, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime())); long millis = System.currentTimeMillis();
ps.setString(2, executor.getUniqueId().toString() + " (" + executor.getName() + ")");
ps.setString(3, product.getAmount() + " x " + LanguageUtils.getItemName(product)); ps.setInt(1, shop.getID());
ps.setString(4, shop.getVendor().getUniqueId().toString() + " (" + shop.getVendor().getName() + ")" + (shop.getShopType() == ShopType.ADMIN ? " (ADMIN)" : "")); ps.setString(2, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(millis));
ps.setString(5, shop.getLocation().getWorld().getName()); ps.setLong(3, millis);
ps.setInt(6, shop.getLocation().getBlockX()); ps.setString(4, executor.getName());
ps.setInt(7, shop.getLocation().getBlockY()); ps.setString(5, executor.getUniqueId().toString());
ps.setInt(8, shop.getLocation().getBlockZ()); ps.setString(6, LanguageUtils.getItemName(product.getItemStack()));
ps.setDouble(9, price); ps.setString(7, Utils.encode(product.getItemStack()));
ps.setString(10, type.toString()); 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(); ps.executeUpdate();
if (callback != null) { if (callback != null) {
@ -409,13 +515,9 @@ public abstract class Database {
BukkitRunnable runnable = new BukkitRunnable() { BukkitRunnable runnable = new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
Calendar cal = Calendar.getInstance(); long time = System.currentTimeMillis() - Config.cleanupEconomyLogDays * 86400000L;
long time = System.currentTimeMillis(); String queryCleanUpLog = "DELETE FROM " + tableLogs + " WHERE time < " + time;
cal.add(Calendar.DATE, -Config.cleanupEconomyLogDays); String queryCleanUpPlayers = "DELETE FROM " + tableLogouts + " WHERE time < " + time;
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);
try (Connection con = dataSource.getConnection(); try (Connection con = dataSource.getConnection();
Statement s = con.createStatement(); Statement s = con.createStatement();
@ -452,36 +554,24 @@ public abstract class Database {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
String vendor = String.format("%s (%s)", player.getUniqueId().toString(), player.getName());
double revenue = 0; double revenue = 0;
try (Connection con = dataSource.getConnection(); try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM shop_log WHERE vendor = ?")) { PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableLogs + " WHERE vendor_uuid = ?")) {
ps.setString(1, vendor); ps.setString(1, player.getUniqueId().toString());
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
if (rs.getString("vendor").equals(vendor)) { long timestamp = rs.getLong("time");
double singleRevenue = rs.getDouble("price"); double singleRevenue = rs.getDouble("price");
ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type")); ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type"));
if (type == ShopBuySellEvent.Type.SELL) { if (type == ShopBuySellEvent.Type.SELL) {
singleRevenue = -singleRevenue; singleRevenue = -singleRevenue;
} }
long timestamp; if (timestamp > logoutTime) {
revenue += singleRevenue;
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;
}
} }
} }
@ -505,18 +595,16 @@ public abstract class Database {
* Log a logout to the database * Log a logout to the database
* *
* @param player Player who logged out * @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} * @param callback Callback that - if succeeded - returns {@code null}
*/ */
public void logLogout(final Player player, final long timestamp, final Callback<Void> callback) { public void logLogout(final Player player, final Callback<Void> callback) {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
try (Connection con = dataSource.getConnection(); 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.setString(1, player.getUniqueId().toString());
ps.setLong(2, timestamp); ps.setLong(2, System.currentTimeMillis());
ps.executeUpdate(); ps.executeUpdate();
if (callback != null) { if (callback != null) {
@ -549,19 +637,14 @@ public abstract class Database {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
String uuid = player.getUniqueId().toString();
try (Connection con = dataSource.getConnection(); try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM player_logout WHERE player = ?")) { PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableLogouts + " WHERE player = ?")) {
ps.setString(1, uuid); ps.setString(1, player.getUniqueId().toString());
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { if (rs.next()) {
if (rs.getString("player").equals(uuid)) { if (callback != null) {
if (callback != null) { callback.callSyncResult(rs.getLong("time"));
callback.callSyncResult(rs.getLong("time"));
}
return;
} }
} }

View File

@ -1,9 +1,9 @@
package de.epiceric.shopchest.utils; package de.epiceric.shopchest.utils;
import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.shop.ShopProduct;
import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.shop.Shop.ShopType;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
@ -18,7 +18,7 @@ public class ClickType {
private static Map<UUID, BukkitTask> playerTimers = new HashMap<>(); private static Map<UUID, BukkitTask> playerTimers = new HashMap<>();
private EnumClickType enumClickType; private EnumClickType enumClickType;
private ItemStack product; private ShopProduct product;
private double buyPrice; private double buyPrice;
private double sellPrice; private double sellPrice;
private ShopType shopType; private ShopType shopType;
@ -27,7 +27,7 @@ public class ClickType {
this.enumClickType = enumClickType; 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.enumClickType = enumClickType;
this.product = product; this.product = product;
this.sellPrice = sellPrice; 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 <b>null</b>. * @return If {@link #getClickType()} returns {@link EnumClickType#CREATE}, this returns the item, the player has hold in his hands, else <b>null</b>.
*/ */
public ItemStack getProduct() { public ShopProduct getProduct() {
return product; return product;
} }

View File

@ -251,6 +251,9 @@ database:
# Either use 'SQLite' or 'MySQL'. Otherwise you will break the plugin! # Either use 'SQLite' or 'MySQL'. Otherwise you will break the plugin!
type: "SQLite" 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... # If the specified type is 'MySQL', here you configure the...
# (You can leave this empty if you're using SQLite) # (You can leave this empty if you're using SQLite)
mysql: mysql: