diff --git a/src/com/minedhype/ishop/CommandShop.java b/src/com/minedhype/ishop/CommandShop.java index 06f976a..2777881 100644 --- a/src/com/minedhype/ishop/CommandShop.java +++ b/src/com/minedhype/ishop/CommandShop.java @@ -1,12 +1,23 @@ package com.minedhype.ishop; import java.net.URL; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Scanner; import java.util.UUID; +import com.bgsoftware.superiorskyblock.api.SuperiorSkyblockAPI; +import com.bgsoftware.superiorskyblock.api.island.Island; +import com.bgsoftware.superiorskyblock.api.island.IslandPrivilege; +import com.bgsoftware.superiorskyblock.api.wrappers.SuperiorPlayer; import com.minedhype.ishop.inventories.InvCreateRow; import com.minedhype.ishop.inventories.InvShop; +import com.palmergames.bukkit.towny.utils.ShopPlotUtil; +import me.angeschossen.lands.api.flags.type.Flags; +import me.angeschossen.lands.api.land.LandWorld; +import me.ryanhamshire.GriefPrevention.ClaimPermission; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; import net.milkbowl.vault.economy.Economy; @@ -19,66 +30,133 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.World; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import com.minedhype.ishop.inventories.InvAdminShop; import com.minedhype.ishop.inventories.InvShopList; +import com.minedhype.ishop.inventories.InvStockList; import com.minedhype.ishop.inventories.InvStock; +import me.ryanhamshire.GriefPrevention.Claim; +import me.ryanhamshire.GriefPrevention.GriefPrevention; public class CommandShop implements CommandExecutor { + + private final Map findCooldown = new HashMap<>(); + static boolean findCommandPublic = iShop.config.getBoolean("publicFindCommand"); + static boolean moveCommandPublic = iShop.config.getBoolean("publicMoveCommand"); + static int findCooldownTime = iShop.config.getInt("findCommandCooldown"); + @Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if(EventShop.shuttingDown) + return true; if(sender instanceof ConsoleCommandSender && args.length > 0) { if(args[0].equalsIgnoreCase("reload")) { reloadShop(null); return true; - } - else { - sender.sendMessage(Messages.NOT_A_PLAYER.toString()); + } else if(args[0].equalsIgnoreCase("deleteid") && args[1] != null) { + deleteShopID(null, args[1]); + return true; + } else if(args[0].equalsIgnoreCase("createlocation") && args.length > 5) { + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> createLocation(null, args[1], args[2], args[3], args[4], args[5])); + return true; + } else if(args[0].equalsIgnoreCase("deletelocation") && args.length > 4) { + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> deleteLocation(null, args[1], args[2], args[3], args[4])); + return true; + } else if(args[0].equalsIgnoreCase("convertdatabase")) { + Shop.convertData(); + return true; + } else if(args[0].equalsIgnoreCase("lockdown")) { + lockdownToggle(null); + return true; + } else { + sender.sendMessage(ChatColor.GOLD + "iShop Console Commands:"); + sender.sendMessage(ChatColor.GRAY + label + " createlocation "); + sender.sendMessage(ChatColor.GRAY + label + " convertdatabase"); + sender.sendMessage(ChatColor.GRAY + label + " deletelocation "); + sender.sendMessage(ChatColor.GRAY + label + " deleteid "); + sender.sendMessage(ChatColor.GRAY + label + " lockdown"); + sender.sendMessage(ChatColor.GRAY + label + " reload"); return false; } } if(!(sender instanceof Player)) { - sender.sendMessage(Messages.NOT_A_PLAYER.toString()); + if(sender instanceof ConsoleCommandSender) { + sender.sendMessage(ChatColor.GOLD + "iShop Console Commands:"); + sender.sendMessage(ChatColor.GRAY + label + " createlocation "); + sender.sendMessage(ChatColor.GRAY + label + " convertdatabase"); + sender.sendMessage(ChatColor.GRAY + label + " deletelocation "); + sender.sendMessage(ChatColor.GRAY + label + " deleteid "); + sender.sendMessage(ChatColor.GRAY + label + " lockdown"); + sender.sendMessage(ChatColor.GRAY + label + " reload"); + } + else + sender.sendMessage(Messages.NOT_A_PLAYER.toString()); return false; } Player player = (Player) sender; + if(iShop.getPlugin().checkLockdown() && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.SHOP_LOCKDOWN.toString()); + return false; + } if(args.length == 0) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> listSubCmd(player, label)); else if(args[0].equalsIgnoreCase("adminshop")) adminShop(player); + else if(args[0].equalsIgnoreCase("copy") && args.length >= 2) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> copyShop(player, args[1])); else if(args[0].equalsIgnoreCase("count") && args.length >= 2) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> count(player, args[1])); else if(args[0].equalsIgnoreCase("create")) createStore(player); else if(args[0].equalsIgnoreCase("createshop") && args.length >= 2) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> createShop(player, args[1])); + else if(args[0].equalsIgnoreCase("createlocation") && args.length > 5) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> createLocation(player, args[1], args[2], args[3], args[4], args[5])); else if(args[0].equalsIgnoreCase("delete")) deleteShop(player); else if(args[0].equalsIgnoreCase("deleteid") && args.length >= 2) deleteShopID(player, args[1]); + else if(args[0].equalsIgnoreCase("deletelocation") && args.length > 4) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> deleteLocation(player, args[1], args[2], args[3], args[4])); + else if(args[0].equalsIgnoreCase("find") && args.length >= 2) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> find(player, args[1])); + else if(args[0].equalsIgnoreCase("findbook") && args.length >= 2) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> findBook(player, args[1])); else if(args[0].equalsIgnoreCase("list") && args.length == 1) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> listShops(player, null)); else if(args[0].equalsIgnoreCase("list") && args.length >= 2) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> listShops(player, args[1])); else if(args[0].equalsIgnoreCase("listadmin")) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> listAdminShops(player)); + else if(args[0].equalsIgnoreCase("liststock")) + listAllStock(player); + else if(args[0].equalsIgnoreCase("lockdown")) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> lockdownToggle(player)); else if(args[0].equalsIgnoreCase("manage") && args.length >= 2) shopManage(player, args[1]); else if(args[0].equalsIgnoreCase("managestock") && args.length == 2) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> manageStock(player, args[1], "1")); else if(args[0].equalsIgnoreCase("managestock") && args.length >= 3) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> manageStock(player, args[1], args[2])); + else if(args[0].equalsIgnoreCase("move") && args.length >= 2) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> moveShop(player, args[1])); else if(args[0].equalsIgnoreCase("out") && args.length == 1) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> outOfStock(player, null)); else if(args[0].equalsIgnoreCase("out") && args.length >= 2) Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> outOfStock(player, args[1])); + else if(args[0].equalsIgnoreCase("purgestock") && args.length == 3 && args[2].equals("confirm")) + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> purgePlayerStock(player, args[1])); else if(args[0].equalsIgnoreCase("reload")) reloadShop(player); + else if(args[0].equalsIgnoreCase("removeallshops") && args.length >= 2) + removeAllShops(player, args[1]); else if(args[0].equalsIgnoreCase("shops")) listAllShops(player); else if(args[0].equalsIgnoreCase("sold") && args.length == 1) @@ -99,29 +177,43 @@ else if(args[0].equalsIgnoreCase("view") && args.length >= 2) private void listSubCmd(Player player, String label) { player.sendMessage(ChatColor.GOLD + "iShop Commands:"); + player.sendMessage(ChatColor.GRAY + "/" + label + " copy "); player.sendMessage(ChatColor.GRAY + "/" + label + " count "); player.sendMessage(ChatColor.GRAY + "/" + label + " create"); player.sendMessage(ChatColor.GRAY + "/" + label + " delete"); player.sendMessage(ChatColor.GRAY + "/" + label + " deleteid "); + if(findCommandPublic || player.hasPermission(Permission.SHOP_ADMIN.toString()) || player.hasPermission(Permission.SHOP_FIND.toString())) { + player.sendMessage(ChatColor.GRAY + "/" + label + " find "); + player.sendMessage(ChatColor.GRAY + "/" + label + " findbook "); + } player.sendMessage(ChatColor.GRAY + "/" + label + " list"); - if(iShop.config.getBoolean("publicListCommand") || player.hasPermission(Permission.SHOP_ADMIN.toString())) + if(iShop.config.getBoolean("publicListCommand") || player.hasPermission(Permission.SHOP_ADMIN.toString()) || player.hasPermission(Permission.SHOP_LIST.toString())) player.sendMessage(ChatColor.GRAY + "/" + label + " list "); - if(iShop.config.getBoolean("publicShopListCommand") || player.hasPermission(Permission.SHOP_ADMIN.toString())) - player.sendMessage(ChatColor.GRAY + "/" + label + " shops"); player.sendMessage(ChatColor.GRAY + "/" + label + " manage "); + if(moveCommandPublic || player.hasPermission(Permission.SHOP_ADMIN.toString()) || player.hasPermission(Permission.SHOP_MOVE.toString())) + player.sendMessage(ChatColor.GRAY + "/" + label + " move "); player.sendMessage(ChatColor.GRAY + "/" + label + " out"); if(player.hasPermission(Permission.SHOP_ADMIN.toString())) player.sendMessage(ChatColor.GRAY + "/" + label + " out "); - if(iShop.config.getBoolean("enableShopSoldMessage")); + if(iShop.config.getBoolean("publicShopListCommand") || player.hasPermission(Permission.SHOP_ADMIN.toString()) || player.hasPermission(Permission.SHOP_SHOPS.toString())) + player.sendMessage(ChatColor.GRAY + "/" + label + " shops"); + if(iShop.config.getBoolean("enableShopSoldMessage")) player.sendMessage(ChatColor.GRAY + "/" + label + " sold "); - player.sendMessage(ChatColor.GRAY + "/" + label + " stock "); + if(iShop.config.getBoolean("enableStockCommand") || player.hasPermission(Permission.SHOP_STOCK.toString()) || player.hasPermission(Permission.SHOP_ADMIN.toString())) + player.sendMessage(ChatColor.GRAY + "/" + label + " stock "); player.sendMessage(ChatColor.GRAY + "/" + label + " view "); if(player.hasPermission(Permission.SHOP_ADMIN.toString())) { player.sendMessage(ChatColor.GRAY + "/" + label + " adminshop"); player.sendMessage(ChatColor.GRAY + "/" + label + " createshop "); + player.sendMessage(ChatColor.GRAY + "/" + label + " createlocation "); + player.sendMessage(ChatColor.GRAY + "/" + label + " deletelocation "); player.sendMessage(ChatColor.GRAY + "/" + label + " listadmin"); + player.sendMessage(ChatColor.GRAY + "/" + label + " liststock"); + player.sendMessage(ChatColor.GRAY + "/" + label + " lockdown"); player.sendMessage(ChatColor.GRAY + "/" + label + " managestock "); + player.sendMessage(ChatColor.GRAY + "/" + label + " purgestock confirm"); player.sendMessage(ChatColor.GRAY + "/" + label + " reload"); + player.sendMessage(ChatColor.GRAY + "/" + label + " removeallshops "); } } @@ -141,7 +233,11 @@ private void count(Player player, String itemName) { } } ItemStack item = new ItemStack(material); - int max = iShop.config.getInt("stockPages"); + int max; + if(InvAdminShop.usePerms) + max = InvAdminShop.permissionMax; + else + max = InvAdminShop.maxPages; int itemAmountCount = 0; for(int i=0; i stockStore = StockShop.getStockShopByOwner(player.getUniqueId(),i); @@ -218,6 +314,48 @@ private void createStore(Player player) { player.sendMessage(Messages.WG_REGION.toString()); return; } + boolean allowShopCreateInClaim = false; + if(iShop.gpLoader != null) { + Claim claim = GriefPrevention.instance.dataStore.getClaimAt(block.getLocation(), false, false, null); + if(claim == null || claim.checkPermission(player, ClaimPermission.Access, null) == null || claim.checkPermission(player, ClaimPermission.Build, null) == null || claim.checkPermission(player, ClaimPermission.Edit, null) == null || claim.checkPermission(player, ClaimPermission.Manage, null) == null || claim.checkPermission(player, ClaimPermission.Container, null) == null) + allowShopCreateInClaim = true; + } + else + allowShopCreateInClaim = true; + if(!allowShopCreateInClaim) { + player.sendMessage(Messages.GP_CLAIM.toString()); + return; + } + boolean allowShopCreateWithinLands = false; + if(iShop.lands != null) { + LandWorld world = iShop.lands.getWorld(player.getWorld()); + if(world != null) + if(world.hasRoleFlag(player.getUniqueId(), block.getLocation(), Flags.BLOCK_PLACE)) + allowShopCreateWithinLands = true; + } + else + allowShopCreateWithinLands = true; + if(!allowShopCreateWithinLands) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + if(iShop.superiorSkyblock2Check) { + Island island = SuperiorSkyblockAPI.getIslandAt(block.getLocation()); + if(island != null) { + IslandPrivilege islandPrivilege = IslandPrivilege.getByName("Build"); + SuperiorPlayer superiorPlayer = SuperiorSkyblockAPI.getPlayer(player.getUniqueId()); + if(!island.hasPermission(superiorPlayer, islandPrivilege)) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } + } + if(iShop.townyCheck) { + if(!ShopPlotUtil.doesPlayerHaveAbilityToEditShopPlot(player, block.getLocation())) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } Optional shop = Shop.getShopByLocation(block.getLocation()); if(shop.isPresent()) { player.sendMessage(Messages.EXISTING_SHOP.toString()); @@ -261,14 +399,12 @@ private void createStore(Player player) { return; } } - Shop newShop = Shop.createShop(block.getLocation(), player.getUniqueId()); + Shop.createShop(block.getLocation(), player.getUniqueId()); player.sendMessage(Messages.SHOP_CREATED.toString()); Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { Optional shops = Shop.getShopByLocation(block.getLocation()); Shop.shopList.put(shops.get().shopId(), player.getUniqueId()); }, 10); - InvAdminShop inv = new InvAdminShop(newShop, player); - inv.open(player, newShop.getOwner()); } private void createShop(Player player, String playerShop) { @@ -337,7 +473,7 @@ private void createShop(Player player, String playerShop) { UUID foundPlayerUUID = null; boolean foundPlayer = false; for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) - if(offlinePlayers.getName().equalsIgnoreCase(playerShop)) { + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(playerShop)) { foundPlayerUUID = offlinePlayers.getUniqueId(); foundPlayer = true; break; @@ -351,30 +487,677 @@ private void createShop(Player player, String playerShop) { } } } - OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(shopOwner); - if(offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) { - player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(shopOwner); + if(offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + return; + } + if(!player.isOp() && !iShop.config.getBoolean("skipPermsCheckForAdminCreateShop")) { + boolean isShopLoc; + if(iShop.wgLoader != null) + isShopLoc = iShop.wgLoader.checkRegion(block); + else + isShopLoc = true; + if(!isShopLoc) { + player.sendMessage(Messages.WG_REGION.toString()); + return; + } + boolean allowShopCreateInClaim = false; + if(iShop.gpLoader != null) { + Claim claim = GriefPrevention.instance.dataStore.getClaimAt(block.getLocation(), false, false, null); + if(claim == null || claim.checkPermission(player, ClaimPermission.Access, null) == null || claim.checkPermission(player, ClaimPermission.Build, null) == null || claim.checkPermission(player, ClaimPermission.Edit, null) == null || claim.checkPermission(player, ClaimPermission.Manage, null) == null || claim.checkPermission(player, ClaimPermission.Container, null) == null) + allowShopCreateInClaim = true; + } + else + allowShopCreateInClaim = true; + if(!allowShopCreateInClaim) { + player.sendMessage(Messages.GP_CLAIM.toString()); + return; + } + boolean allowShopCreateWithinLands = false; + if(iShop.lands != null) { + LandWorld world = iShop.lands.getWorld(player.getWorld()); + if(world != null) + if(world.hasRoleFlag(player.getUniqueId(), block.getLocation(), Flags.BLOCK_PLACE)) + allowShopCreateWithinLands = true; + } + else + allowShopCreateWithinLands = true; + if(!allowShopCreateWithinLands) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + if(iShop.superiorSkyblock2Check) { + Island island = SuperiorSkyblockAPI.getIslandAt(block.getLocation()); + if(island != null) { + IslandPrivilege islandPrivilege = IslandPrivilege.getByName("Build"); + SuperiorPlayer superiorPlayer = SuperiorSkyblockAPI.getPlayer(player.getUniqueId()); + if(!island.hasPermission(superiorPlayer, islandPrivilege)) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } + } + if(iShop.townyCheck) { + if(!ShopPlotUtil.doesPlayerHaveAbilityToEditShopPlot(player, block.getLocation())) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } + } + Optional shop = Shop.getShopByLocation(block.getLocation()); + if(!shop.isPresent()) { + Shop.createShop(block.getLocation(), shopOwner); + player.sendMessage(Messages.PLAYER_SHOP_CREATED.toString().replaceAll("%p", playerShop)); + final UUID shopOwnerFinal = shopOwner; + Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { + Optional shops = Shop.getShopByLocation(block.getLocation()); + Shop.shopList.put(shops.get().shopId(), shopOwnerFinal); + }, 10); + } else { player.sendMessage(Messages.EXISTING_SHOP.toString()); } + } + + private void copyShop(Player player, String shopId) { + if(iShop.config.getBoolean("usePermissions") && !player.hasPermission(Permission.SHOP_CREATE.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(!iShop.config.getBoolean("enableShopBlock")) { + player.sendMessage(Messages.DISABLED_SHOP_BLOCK.toString()); + return; + } + Block block = player.getTargetBlockExact(5); + if(block == null) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + if(iShop.config.getBoolean("disableShopInWorld")) { + List disabledWorldsList = iShop.config.getStringList("disabledWorldList"); + for(String disabledWorlds:disabledWorldsList) { + if(disabledWorlds != null && block.getWorld().getName().equals(disabledWorlds)) { + player.sendMessage(Messages.SHOP_WORLD_DISABLED.toString()); + return; + } + } + } + String shopBlock = iShop.config.getString("shopBlock"); + Material match = Material.matchMaterial(shopBlock); + if(match == null) { + try { + match = Material.matchMaterial(shopBlock.split("minecraft:")[1].toUpperCase()); + } catch(Exception ignored) { } + if(match == null) + match = Material.BARREL; + } + if(!EventShop.multipleShopBlocks) { + if(!block.getType().equals(match)) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + } else { + boolean shopMatch = false; + for(String shopBlocks:EventShop.multipleShopBlock) { + Material shopListBlocks = Material.matchMaterial(shopBlocks); + if(shopListBlocks != null && block.getType().equals(shopListBlocks)) { + shopMatch = true; + break; + } + } + if(!block.getType().equals(match) && !shopMatch) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + } + boolean isShopLoc; + if(iShop.wgLoader != null) + isShopLoc = iShop.wgLoader.checkRegion(block); + else + isShopLoc = true; + if(!isShopLoc) { + player.sendMessage(Messages.WG_REGION.toString()); + return; + } + boolean allowShopCreateInClaim = false; + if(iShop.gpLoader != null) { + Claim claim = GriefPrevention.instance.dataStore.getClaimAt(block.getLocation(), false, false, null); + if(claim == null || claim.checkPermission(player, ClaimPermission.Access, null) == null || claim.checkPermission(player, ClaimPermission.Build, null) == null || claim.checkPermission(player, ClaimPermission.Edit, null) == null || claim.checkPermission(player, ClaimPermission.Manage, null) == null || claim.checkPermission(player, ClaimPermission.Container, null) == null) + allowShopCreateInClaim = true; + } + else + allowShopCreateInClaim = true; + if(!allowShopCreateInClaim) { + player.sendMessage(Messages.GP_CLAIM.toString()); + return; + } + boolean allowShopCreateWithinLands = false; + if(iShop.lands != null) { + LandWorld world = iShop.lands.getWorld(player.getWorld()); + if(world != null) + if(world.hasRoleFlag(player.getUniqueId(), block.getLocation(), Flags.BLOCK_PLACE)) + allowShopCreateWithinLands = true; + } + else + allowShopCreateWithinLands = true; + if(!allowShopCreateWithinLands) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + if(iShop.superiorSkyblock2Check) { + Island island = SuperiorSkyblockAPI.getIslandAt(block.getLocation()); + if(island != null) { + IslandPrivilege islandPrivilege = IslandPrivilege.getByName("Build"); + SuperiorPlayer superiorPlayer = SuperiorSkyblockAPI.getPlayer(player.getUniqueId()); + if(!island.hasPermission(superiorPlayer, islandPrivilege)) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } + } + if(iShop.townyCheck) { + if(!ShopPlotUtil.doesPlayerHaveAbilityToEditShopPlot(player, block.getLocation())) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } + Optional shop = Shop.getShopByLocation(block.getLocation()); + if(shop.isPresent()) { + player.sendMessage(Messages.EXISTING_SHOP.toString()); + return; + } + boolean limitShops; + int numShops = Shop.getNumShops(player.getUniqueId()); + if(iShop.config.getBoolean("usePermissions")) { + int maxShops = 0; + String permPrefix = Permission.SHOP_LIMIT_PREFIX.toString(); + for(PermissionAttachmentInfo attInfo : player.getEffectivePermissions()) { + String perm = attInfo.getPermission(); + if(perm.startsWith(permPrefix)) { + int num; + try { + num = Integer.parseInt(perm.substring(perm.lastIndexOf(".")+1)); + } catch(Exception e) { num = 0; } + if(num > maxShops) + maxShops = num; + } + } + limitShops = numShops >= maxShops; + } + else { + int numConfig = iShop.config.getInt("defaultShopLimit"); + limitShops = numShops >= numConfig && numConfig >= 0; + } + if(player.hasPermission(Permission.SHOP_LIMIT_BYPASS.toString())) + limitShops = false; + if(limitShops) { + player.sendMessage(Messages.SHOP_MAX.toString()); + return; + } + double cost = iShop.config.getDouble("createCost"); + Optional economy = iShop.getEconomy(); + if(cost > 0 && economy.isPresent()) { + OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(player.getUniqueId()); + EconomyResponse res = economy.get().withdrawPlayer(offPlayer, cost); + if(!res.transactionSuccess()) { + player.sendMessage(Messages.SHOP_CREATE_NO_MONEY.toString()+cost); + return; + } + } + int shopNumber = Integer.parseInt(shopId); + Optional copyShop = Shop.getShopById(shopNumber); + if(!copyShop.isPresent()) { + player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); + return; + } + Optional shopCopy = Shop.getShopByLocation(block.getLocation()); + if(!shopCopy.isPresent()) { + Shop.duplicateShop(block.getLocation(),player.getUniqueId(), shopNumber); + player.sendMessage(Messages.SHOP_CREATED.toString()); + Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { + Optional shops = Shop.getShopByLocation(block.getLocation()); + Shop.shopList.put(shops.get().shopId(), player.getUniqueId()); + }, 10); + } else { player.sendMessage(Messages.EXISTING_SHOP.toString()); } + } + + private void createLocation(Player player, String playerShop, String x, String y, String z, String worldString) { + if(player != null && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(!iShop.config.getBoolean("enableShopBlock")) { + if(player != null) + player.sendMessage(Messages.DISABLED_SHOP_BLOCK.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.DISABLED_SHOP_BLOCK.toString()); + return; + } + String world; + if(worldString != null) + world = worldString; + else + world = "world"; + + int xLoc,yLoc,zLoc; + try { + xLoc = Integer.parseInt(x); + yLoc = Integer.parseInt(y); + zLoc = Integer.parseInt(z); + } + catch(Exception e) { + if(player != null) + player.sendMessage(Messages.SHOP_LOCATION_ERROR_NUM.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_LOCATION_ERROR_NUM.toString()); + return; + } + World worldLoc = Bukkit.getWorld(world); + Location blockLoc; + if(worldLoc != null) + blockLoc = new Location(worldLoc, xLoc, yLoc, zLoc); + else { + if(player != null) + player.sendMessage(Messages.SHOP_LOCATION_ERROR_WORLD.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_LOCATION_ERROR_WORLD.toString()); + return; + } + Block block = Bukkit.getWorld(world).getBlockAt(blockLoc); + if(iShop.config.getBoolean("disableShopInWorld")) { + List disabledWorldsList = iShop.config.getStringList("disabledWorldList"); + for(String disabledWorlds:disabledWorldsList) { + if(disabledWorlds != null && block.getWorld().getName().equals(disabledWorlds)) { + if(player != null) + player.sendMessage(Messages.SHOP_WORLD_DISABLED.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_WORLD_DISABLED.toString()); + return; + } + } + } + String shopBlock = iShop.config.getString("shopBlock"); + Material match = Material.matchMaterial(shopBlock); + if(match == null) { + try { + match = Material.matchMaterial(shopBlock.split("minecraft:")[1].toUpperCase()); + } catch(Exception ignored) { } + if(match == null) + match = Material.BARREL; + } + if(!EventShop.multipleShopBlocks) { + if(!block.getType().equals(match)) { + if(player != null) + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + } else { + boolean shopMatch = false; + for(String shopBlocks:EventShop.multipleShopBlock) { + Material shopListBlocks = Material.matchMaterial(shopBlocks); + if(shopListBlocks != null && block.getType().equals(shopListBlocks)) { + shopMatch = true; + break; + } + } + if(!block.getType().equals(match) && !shopMatch) { + if(player != null) + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + } + UUID shopOwner; + if(playerShop == null) { + if(player != null) + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.NO_PLAYER_FOUND.toString()); + return; + } else { + Player playerInGame = Bukkit.getPlayer(playerShop); + if(playerInGame != null && playerInGame.isOnline()) + shopOwner = playerInGame.getUniqueId(); + else { + try { + shopOwner = getUUID(playerShop); + } catch (Exception e) { + UUID foundPlayerUUID = null; + boolean foundPlayer = false; + for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(playerShop)) { + foundPlayerUUID = offlinePlayers.getUniqueId(); + foundPlayer = true; + break; + } + if(!foundPlayer) { + if(player != null) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + } + else + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + return; + } + shopOwner = foundPlayerUUID; + } + } + } + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(shopOwner); + if(offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) { + if(player != null) + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.NO_PLAYER_FOUND.toString()); + return; + } + Optional shop = Shop.getShopByLocation(block.getLocation()); + if(!shop.isPresent()) { + Shop.createShop(block.getLocation(), shopOwner); + if(player != null) + player.sendMessage(Messages.SHOP_CREATED_LOC.toString().replaceAll("%p", playerShop).replaceAll("%w", world).replaceAll("%x", x).replaceAll("%y", y).replaceAll("%z", z)); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_CREATED_LOC.toString().replaceAll("%p", playerShop).replaceAll("%w", world).replaceAll("%x", x).replaceAll("%y", y).replaceAll("%z", z)); + final UUID shopOwnerFinal = shopOwner; + Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { + Optional shops = Shop.getShopByLocation(block.getLocation()); + Shop.shopList.put(shops.get().shopId(), shopOwnerFinal); + }, 10); + } else { + if(player != null) + player.sendMessage(Messages.EXISTING_SHOP.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.EXISTING_SHOP.toString().replaceAll("%p", playerShop)); + } + } + + private void adminShop(Player player) { + if(!player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(!EventShop.adminShopEnabled) { + player.sendMessage(Messages.ADMIN_SHOP_DISABLED.toString()); + return; + } + Block block = player.getTargetBlockExact(5); + if(block == null) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + String shopBlock = iShop.config.getString("shopBlock"); + Material match = Material.matchMaterial(shopBlock); + if(match == null) { + try { + match = Material.matchMaterial(shopBlock.split("minecraft:")[1].toUpperCase()); + } catch(Exception ignored) { } + if(match == null) + match = Material.BARREL; + } + if(!EventShop.multipleShopBlocks) { + if(!block.getType().equals(match)) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + } else { + boolean shopMatch = false; + for(String shopBlocks:EventShop.multipleShopBlock) { + Material shopListBlocks = Material.matchMaterial(shopBlocks); + if(shopListBlocks != null && block.getType().equals(shopListBlocks)) { + shopMatch = true; + break; + } + } + if(!block.getType().equals(match) && !shopMatch) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + } + Optional shop = Shop.getShopByLocation(block.getLocation()); + if(shop.isPresent()) { + player.sendMessage(Messages.EXISTING_SHOP.toString()); + return; + } + String adminHead; + try { adminHead = iShop.config.getString("adminPlayerHeadShops"); } + catch(Exception e) { adminHead = "00000000-0000-0000-0000-000000000000"; } + Shop newShop = Shop.createShop(block.getLocation(), UUID.fromString(adminHead), true); + player.sendMessage(Messages.SHOP_CREATED.toString()); + if(iShop.config.getBoolean("adminShopPublic")) { + final String adminHeadFinal = adminHead; + Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { + Optional shops = Shop.getShopByLocation(block.getLocation()); + Shop.shopList.put(shops.get().shopId(), UUID.fromString(adminHeadFinal)); + }, 10); + } + InvAdminShop inv = new InvAdminShop(newShop, player); + inv.open(player, newShop.getOwner()); + } + + private void deleteShop(Player player) { + Block block = player.getTargetBlockExact(5); + if(block == null) { + player.sendMessage(Messages.TARGET_MISMATCH.toString()); + return; + } + Optional shop = Shop.getShopByLocation(block.getLocation()); + if(!shop.isPresent()) { + player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); + return; + } + if(!shop.get().isOwner(player.getUniqueId()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.SHOP_NO_SELF.toString()); + return; + } + if(shop.get().isAdmin() && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { + player.sendMessage(Messages.SHOP_BUSY.toString()); + return; + } + double cost = iShop.config.getDouble("returnAmount"); + Optional economy = iShop.getEconomy(); + if(cost > 0 && economy.isPresent()) { + OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(shop.get().getOwner()); + economy.get().depositPlayer(offPlayer, cost); + } + Shop.shopList.remove(shop.get().shopId()); + shop.get().deleteShop(); + player.sendMessage(Messages.SHOP_DELETED.toString()); + } + + private void deleteShopID(Player player, String shopId) { + int sID; + try { + sID = Integer.parseInt(shopId); + } catch (Exception e) { sID = -1; } + if(sID < 0) { + if(player != null) + player.sendMessage(Messages.SHOP_ID_INTEGER.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_ID_INTEGER.toString()); + return; + } + Optional shop = Shop.getShopById(sID); + if(!shop.isPresent()) { + if(player != null) + player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_NOT_FOUND.toString()); + return; + } + if(player != null && !shop.get().isOwner(player.getUniqueId()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.SHOP_NO_SELF.toString()); + return; + } + if(shop.get().isAdmin() && player != null && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { + if(player != null) + player.sendMessage(Messages.SHOP_BUSY.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_BUSY.toString()); + return; + } + double cost = iShop.config.getDouble("returnAmount"); + Optional economy = iShop.getEconomy(); + if(cost > 0 && economy.isPresent()) { + OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(shop.get().getOwner()); + economy.get().depositPlayer(offPlayer, cost); + } + Shop.shopList.remove(shop.get().shopId()); + shop.get().deleteShop(); + if(player != null) + player.sendMessage(Messages.SHOP_IDDELETED.toString().replaceAll("%id", shopId)); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_IDDELETED.toString().replaceAll("%id", shopId)); + } + + private void deleteLocation(Player player, String x, String y, String z, String worldString) { + if(player != null && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + String world; + if(worldString != null) + world = worldString; + else + world = "world"; + + int xLoc,yLoc,zLoc; + try { + xLoc = Integer.parseInt(x); + yLoc = Integer.parseInt(y); + zLoc = Integer.parseInt(z); + } + catch(Exception e) { + if(player != null) + player.sendMessage(Messages.SHOP_LOCATION_ERROR_NUM.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_LOCATION_ERROR_NUM.toString()); + return; + } + World worldLoc = Bukkit.getWorld(world); + Location blockLoc; + if(worldLoc != null) + blockLoc = new Location(worldLoc, xLoc, yLoc, zLoc); + else { + if(player != null) + player.sendMessage(Messages.SHOP_LOCATION_ERROR_WORLD.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_LOCATION_ERROR_WORLD.toString()); + return; + } + + Optional shop = Shop.getShopByLocation(blockLoc); + if(!shop.isPresent()) { + if(player != null) + player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_NOT_FOUND.toString()); + return; + } + if(player != null && !shop.get().isOwner(player.getUniqueId()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.SHOP_NO_SELF.toString()); + return; + } + if(player != null && shop.get().isAdmin() && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { + if(player != null) + player.sendMessage(Messages.SHOP_BUSY.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_BUSY.toString()); + return; + } + OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(shop.get().getOwner()); + String shopOwner = offPlayer.getName(); + double cost = iShop.config.getDouble("returnAmount"); + Optional economy = iShop.getEconomy(); + if(cost > 0 && economy.isPresent()) + economy.get().depositPlayer(offPlayer, cost); + Shop.shopList.remove(shop.get().shopId()); + shop.get().deleteShop(); + if(player != null) + player.sendMessage(Messages.SHOP_DELETED_LOC.toString().replaceAll("%p", shopOwner).replaceAll("%w", world).replaceAll("%x", x).replaceAll("%y", y).replaceAll("%z", z)); + else + Bukkit.getConsoleSender().sendMessage(Messages.SHOP_DELETED_LOC.toString().replaceAll("%p", shopOwner).replaceAll("%w", world).replaceAll("%x", x).replaceAll("%y", y).replaceAll("%z", z)); + } + + private void find(Player player, String itemName) { + if(!findCommandPublic && !player.hasPermission(Permission.SHOP_FIND.toString()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(findCooldownTime > 0 && !player.hasPermission(Permission.SHOP_ADMIN.toString()) && !player.hasPermission(Permission.SHOP_BYPASS_FIND_CD.toString())) { + if(findCooldown.containsKey(player.getUniqueId())) { + long secondsLeft = (((findCooldown.get(player.getUniqueId()))/1000)+findCooldownTime) - (System.currentTimeMillis()/1000); + if(secondsLeft > 0) { + player.sendMessage(Messages.SHOP_FIND_COOLDOWN.toString().replaceAll("%time", String.valueOf(secondsLeft))); + return; + } + } + findCooldown.put(player.getUniqueId(), System.currentTimeMillis()); + } + Material material = Material.matchMaterial(itemName); + if(material == null) { + try { + material = Material.matchMaterial(itemName.split("minecraft:")[1].toUpperCase()); + } catch(Exception ignored) { } + if(material == null) { + player.sendMessage(Messages.SHOP_FIND_ERROR.toString()); + return; + } + } + if(material.isAir()) { + player.sendMessage(Messages.SHOP_FIND_ERROR.toString()); + return; + } + ItemStack item = new ItemStack(material); + Shop.findItem(player, item, itemName); + } + + private void findBook(Player player, String bookName) { + if(!findCommandPublic && !player.hasPermission(Permission.SHOP_FIND.toString()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(findCooldownTime > 0 && !player.hasPermission(Permission.SHOP_ADMIN.toString()) && !player.hasPermission(Permission.SHOP_BYPASS_FIND_CD.toString())) { + if(findCooldown.containsKey(player.getUniqueId())) { + long secondsLeft = (((findCooldown.get(player.getUniqueId()))/1000)+findCooldownTime) - (System.currentTimeMillis()/1000); + if(secondsLeft > 0) { + player.sendMessage(Messages.SHOP_FIND_COOLDOWN.toString().replaceAll("%time", String.valueOf(secondsLeft))); + return; + } + } + findCooldown.put(player.getUniqueId(), System.currentTimeMillis()); + } + if(!TabComplete.enchantments.contains(bookName)) { + player.sendMessage(Messages.SHOP_FIND_ERROR.toString()); return; } - Optional shop = Shop.getShopByLocation(block.getLocation()); - if(!shop.isPresent()) { - Shop.createShop(block.getLocation(), shopOwner); - player.sendMessage(Messages.PLAYER_SHOP_CREATED.toString().replaceAll("%p", playerShop)); - final UUID shopOwnerFinal = shopOwner; - Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { - Optional shops = Shop.getShopByLocation(block.getLocation()); - Shop.shopList.put(shops.get().shopId(), shopOwnerFinal); - }, 10); - } else { player.sendMessage(Messages.EXISTING_SHOP.toString()); } + Shop.findBook(player, bookName); } - private void adminShop(Player player) { - if(!player.hasPermission(Permission.SHOP_ADMIN.toString())) { + private void moveShop(Player player, String shopId) { + if(!moveCommandPublic && !player.hasPermission(Permission.SHOP_MOVE.toString()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { player.sendMessage(Messages.NO_PERMISSION.toString()); return; } - if(!EventShop.adminShopEnabled) { - player.sendMessage(Messages.ADMIN_SHOP_DISABLED.toString()); + int sId; + try { + sId = Integer.parseInt(shopId); + } catch (Exception e) { sId = -1; } + if(sId < 1) { + if(player != null) + player.sendMessage(Messages.SHOP_ID_INTEGER.toString()); return; } Block block = player.getTargetBlockExact(5); @@ -382,6 +1165,14 @@ private void adminShop(Player player) { player.sendMessage(Messages.TARGET_MISMATCH.toString()); return; } + if(iShop.config.getBoolean("disableShopInWorld")) { + List disabledWorldsList = iShop.config.getStringList("disabledWorldList"); + for(String disabledWorlds:disabledWorldsList) + if(disabledWorlds != null && block.getWorld().getName().equals(disabledWorlds)) { + player.sendMessage(Messages.SHOP_WORLD_DISABLED.toString()); + return; + } + } String shopBlock = iShop.config.getString("shopBlock"); Material match = Material.matchMaterial(shopBlock); if(match == null) { @@ -410,34 +1201,63 @@ private void adminShop(Player player) { return; } } - Optional shop = Shop.getShopByLocation(block.getLocation()); - if(shop.isPresent()) { - player.sendMessage(Messages.EXISTING_SHOP.toString()); - return; - } - String adminHead; - try { adminHead = iShop.config.getString("adminPlayerHeadShops"); } - catch(Exception e) { adminHead = "00000000-0000-0000-0000-000000000000"; } - Shop newShop = Shop.createShop(block.getLocation(), UUID.fromString(adminHead), true); - player.sendMessage(Messages.SHOP_CREATED.toString()); - if(iShop.config.getBoolean("adminShopPublic")) { - final String adminHeadFinal = adminHead; - Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(iShop.getPlugin(), () -> { - Optional shops = Shop.getShopByLocation(block.getLocation()); - Shop.shopList.put(shops.get().shopId(), UUID.fromString(adminHeadFinal)); - }, 10); + if(!player.isOp()) { + boolean isShopLoc; + if(iShop.wgLoader != null) + isShopLoc = iShop.wgLoader.checkRegion(block); + else + isShopLoc = true; + if(!isShopLoc) { + player.sendMessage(Messages.WG_REGION.toString()); + return; + } + boolean allowShopCreateInClaim = false; + if(iShop.gpLoader != null) { + Claim claim = GriefPrevention.instance.dataStore.getClaimAt(block.getLocation(), false, false, null); + if(claim == null || claim.checkPermission(player, ClaimPermission.Access, null) == null || claim.checkPermission(player, ClaimPermission.Build, null) == null || claim.checkPermission(player, ClaimPermission.Edit, null) == null || claim.checkPermission(player, ClaimPermission.Manage, null) == null || claim.checkPermission(player, ClaimPermission.Container, null) == null) + allowShopCreateInClaim = true; + } else + allowShopCreateInClaim = true; + if(!allowShopCreateInClaim) { + player.sendMessage(Messages.GP_CLAIM.toString()); + return; + } + boolean allowShopCreateWithinLands = false; + if(iShop.lands != null) { + LandWorld world = iShop.lands.getWorld(player.getWorld()); + if(world != null) + if(world.hasRoleFlag(player.getUniqueId(), block.getLocation(), Flags.BLOCK_PLACE)) + allowShopCreateWithinLands = true; + } + else + allowShopCreateWithinLands = true; + if(!allowShopCreateWithinLands) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + if(iShop.superiorSkyblock2Check) { + Island island = SuperiorSkyblockAPI.getIslandAt(block.getLocation()); + if(island != null) { + IslandPrivilege islandPrivilege = IslandPrivilege.getByName("Build"); + SuperiorPlayer superiorPlayer = SuperiorSkyblockAPI.getPlayer(player.getUniqueId()); + if(!island.hasPermission(superiorPlayer, islandPrivilege)) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } + } + } + if(iShop.townyCheck) + if(!ShopPlotUtil.doesPlayerHaveAbilityToEditShopPlot(player, block.getLocation())) { + player.sendMessage(Messages.NO_SHOP_CREATE_PERMISSION.toString()); + return; + } } - InvAdminShop inv = new InvAdminShop(newShop, player); - inv.open(player, newShop.getOwner()); - } - - private void deleteShop(Player player) { - Block block = player.getTargetBlockExact(5); - if(block == null) { - player.sendMessage(Messages.TARGET_MISMATCH.toString()); + Optional shopLocation = Shop.getShopByLocation(block.getLocation()); + if(shopLocation.isPresent()) { + player.sendMessage(Messages.EXISTING_SHOP.toString()); return; } - Optional shop = Shop.getShopByLocation(block.getLocation()); + Optional shop = Shop.getShopById(sId); if(!shop.isPresent()) { player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); return; @@ -454,56 +1274,51 @@ private void deleteShop(Player player) { player.sendMessage(Messages.SHOP_BUSY.toString()); return; } - double cost = iShop.config.getDouble("returnAmount"); - Optional economy = iShop.getEconomy(); - if(cost > 0 && economy.isPresent()) { - OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(shop.get().getOwner()); - economy.get().depositPlayer(offPlayer, cost); - } - Shop.shopList.remove(shop.get().shopId()); - shop.get().deleteShop(); - player.sendMessage(Messages.SHOP_DELETED.toString()); + final Location oldShopBlock = shop.get().getLocation(); + final Location newShopBlock = block.getLocation(); + final int shopID = shop.get().shopId(); + Shop.moveShop(oldShopBlock, newShopBlock, player, shopID); } - private void deleteShopID(Player player, String shopId) { - int sID; - try { - sID = Integer.parseInt(shopId); - } catch (Exception e) { sID = -1; } - if(sID < 0) { - player.sendMessage(Messages.SHOP_ID_INTEGER.toString()); - return; - } - Optional shop = Shop.getShopById(sID); - if(!shop.isPresent()) { - player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); - return; - } - if(!shop.get().isOwner(player.getUniqueId()) && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { - player.sendMessage(Messages.SHOP_NO_SELF.toString()); - return; - } - if(shop.get().isAdmin() && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + private void removeAllShops(Player player, String playerName) { + if(!player.hasPermission(Permission.SHOP_ADMIN.toString())) { player.sendMessage(Messages.NO_PERMISSION.toString()); return; } - if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { + UUID sOwner; + Player playerInGame = Bukkit.getPlayer(playerName); + if(playerInGame != null && playerInGame.isOnline()) + sOwner = playerInGame.getUniqueId(); + else { + try { + sOwner = getUUID(playerName); + } catch (Exception e) { + UUID foundPlayerUUID = null; + boolean foundPlayer = false; + for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(playerName)) { + foundPlayerUUID = offlinePlayers.getUniqueId(); + foundPlayer = true; + break; + } + if(!foundPlayer) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + return; + } + sOwner = foundPlayerUUID; + } + } + if(InvStock.inShopInv.containsValue(sOwner)) { player.sendMessage(Messages.SHOP_BUSY.toString()); return; } - double cost = iShop.config.getDouble("returnAmount"); - Optional economy = iShop.getEconomy(); - if(cost > 0 && economy.isPresent()) { - OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(shop.get().getOwner()); - economy.get().depositPlayer(offPlayer, cost); - } - Shop.shopList.remove(shop.get().shopId()); - shop.get().deleteShop(); - player.sendMessage(Messages.SHOP_IDDELETED.toString().replaceAll("%id", shopId)); + final UUID shopOwner = sOwner; + Bukkit.getServer().getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> Shop.removeAllPlayersShops(player, shopOwner, playerName)); } private void listShops(Player player, String playerName) { - if(playerName != null && !iShop.config.getBoolean("publicListCommand") && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + if(playerName != null && !iShop.config.getBoolean("publicListCommand") && (!player.hasPermission(Permission.SHOP_ADMIN.toString()) || !player.hasPermission(Permission.SHOP_LIST.toString()))) { player.sendMessage(Messages.NO_PERMISSION.toString()); return; } @@ -512,12 +1327,28 @@ private void listShops(Player player, String playerName) { sOwner = player.getUniqueId(); playerName = player.getDisplayName(); } else { - try { - sOwner = getUUID(playerName); - } catch (Exception e) { - player.sendMessage(Messages.NO_PLAYER_SHOP.toString()); - Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_SHOP); - return; + Player playerInGame = Bukkit.getPlayer(playerName); + if(playerInGame != null && playerInGame.isOnline()) + sOwner = playerInGame.getUniqueId(); + else { + try { + sOwner = getUUID(playerName); + } catch (Exception e) { + UUID foundPlayerUUID = null; + boolean foundPlayer = false; + for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(playerName)) { + foundPlayerUUID = offlinePlayers.getUniqueId(); + foundPlayer = true; + break; + } + if(!foundPlayer) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + return; + } + sOwner = foundPlayerUUID; + } } } Shop.getShopList(player, sOwner, playerName); @@ -532,7 +1363,7 @@ private void listAdminShops(Player player) { } private void listAllShops(Player player) { - if(!InvShop.listAllShops && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + if(!InvShop.listAllShops && (!player.hasPermission(Permission.SHOP_ADMIN.toString()) || !player.hasPermission(Permission.SHOP_SHOPS.toString()))) { player.sendMessage(Messages.SHOP_LIST_DISABLED.toString()); return; } @@ -541,6 +1372,16 @@ private void listAllShops(Player player) { inv.open(player); } + private void listAllStock(Player player) { + if(!player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + InvStockList inv = InvStockList.setStockTitle("List of Players with Stock"); + inv.setPag(0); + inv.open(player); + } + private void stockShop(Player player, String page) { if(!InvAdminShop.stockCommandEnabled && !player.hasPermission(Permission.SHOP_ADMIN.toString()) && !player.hasPermission(Permission.SHOP_STOCK.toString())) { player.sendMessage(Messages.STOCK_COMMAND_DISABLED.toString()); @@ -563,14 +1404,28 @@ private void stockShop(Player player, String page) { return; } openPage--; - int stockPages = iShop.config.getInt("stockPages"); - if(openPage > 0 && openPage > stockPages-1) - openPage = stockPages-1; + int maxStockPages = InvAdminShop.maxPages; + if(InvAdminShop.usePerms) { + String permPrefix = Permission.SHOP_STOCK_PREFIX.toString(); + int maxPermPages = InvAdminShop.permissionMax; + boolean permissionFound = false; + for(int i=maxPermPages; i>0; i--) + if(player.hasPermission(permPrefix + i)) { + maxStockPages = i; + permissionFound = true; + break; + } + if(!permissionFound) + maxStockPages = maxPermPages; + } + if(openPage > 0 && openPage > maxStockPages-1) + openPage = maxStockPages-1; if(InvStock.inShopInv.containsValue(player.getUniqueId())) { player.sendMessage(Messages.SHOP_BUSY.toString()); return; } else { InvStock.inShopInv.put(player, player.getUniqueId()); } InvStock inv = InvStock.getInvStock(player.getUniqueId()); + inv.setMaxPages(maxStockPages); inv.setPag(openPage); inv.open(player); } @@ -607,11 +1462,18 @@ private void reloadShop(Player player) { EventShop.multipleShopBlocks = iShop.config.getBoolean("multipleShopBlocks"); EventShop.multipleStockBlocks = iShop.config.getBoolean("multipleStockBlocks"); InvAdminShop.remoteManage = iShop.config.getBoolean("remoteManage"); + InvAdminShop.maxPages = iShop.config.getInt("stockPages"); + InvAdminShop.permissionMax = iShop.config.getInt("maxStockPages"); InvAdminShop.stockCommandEnabled = iShop.config.getBoolean("enableStockCommand"); InvAdminShop.stockGUIShop = iShop.config.getBoolean("enableStockAccessFromShopGUI"); + InvAdminShop.usePerms = iShop.config.getBoolean("usePermissions"); InvCreateRow.disabledItemList = iShop.config.getStringList("disabledItemsList"); InvCreateRow.itemsDisabled = iShop.config.getBoolean("disabledItems"); + InvCreateRow.preventDupeTrades = iShop.config.getBoolean("preventDuplicates"); + InvCreateRow.preventAllDupeTrades = iShop.config.getBoolean("preventAllDuplicates"); + InvCreateRow.strictStock = iShop.config.getBoolean("strictStock"); InvShop.listAllShops = iShop.config.getBoolean("publicShopListCommand"); + InvStock.showStockListAdmin = iShop.config.getBoolean("showStockListItemForAdmin"); Shop.showOwnedShops = iShop.config.getBoolean("publicShopListShowsOwned"); Shop.shopEnabled = iShop.config.getBoolean("enableShopBlock"); Shop.shopNotifications = iShop.config.getBoolean("enableShopNotifications"); @@ -619,8 +1481,15 @@ private void reloadShop(Player player) { Shop.particleEffects = iShop.config.getBoolean("showParticles"); Shop.maxDays = iShop.config.getInt("maxInactiveDays"); Shop.deletePlayerShop = iShop.config.getBoolean("deleteBlock"); + Shop.saveEmptyShops = iShop.config.getBoolean("saveEmptyShops"); Shop.stockMessages = iShop.config.getBoolean("enableShopSoldMessage"); Shop.exemptExpiringList = iShop.config.getStringList("exemptExpiringShops"); + Shop.stockDeleteWhenShopsExpire = iShop.config.getBoolean("deleteStockWhenShopsExpire"); + StockShop.exemptStockExpiringList = iShop.config.getStringList("exemptExpiringStock"); + StockShop.maxStockDays = iShop.config.getInt("maxStockInactiveDays"); + findCommandPublic = iShop.config.getBoolean("publicFindCommand"); + findCooldownTime = iShop.config.getInt("findCommandCooldown"); + moveCommandPublic = iShop.config.getBoolean("publicMoveCommand"); }); } @@ -635,7 +1504,7 @@ private static UUID getUUID(String name) throws Exception { } private static void shopManage(Player player, String shopID) { - if(!InvAdminShop.remoteManage && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + if(!InvAdminShop.remoteManage && (!player.hasPermission(Permission.SHOP_ADMIN.toString()) || !player.hasPermission(Permission.SHOP_REMOTEMANAGE.toString()))) { player.sendMessage(Messages.SHOP_REMOTE.toString()); return; } @@ -671,7 +1540,7 @@ private static void shopManage(Player player, String shopID) { } private static void viewShop(Player player, String shopId) { - if(!iShop.config.getBoolean("remoteShopping") && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + if(!iShop.config.getBoolean("remoteShopping") && (!player.hasPermission(Permission.SHOP_ADMIN.toString()) || !player.hasPermission(Permission.SHOP_REMOTESHOPPING.toString()))) { player.sendMessage(Messages.SHOP_NO_REMOTE.toString()); return; } @@ -688,7 +1557,7 @@ private static void viewShop(Player player, String shopId) { player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); return; } - if(shop.get().getOwner().equals(player.getUniqueId()) && !InvAdminShop.remoteManage && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + if(shop.get().getOwner().equals(player.getUniqueId()) && !InvAdminShop.remoteManage && (!player.hasPermission(Permission.SHOP_ADMIN.toString()) || !player.hasPermission(Permission.SHOP_REMOTEMANAGE.toString()))) { player.sendMessage(Messages.SHOP_REMOTE.toString()); return; } @@ -700,7 +1569,7 @@ private static void viewShop(Player player, String shopId) { player.sendMessage(Messages.SHOP_BUSY.toString()); return; } - if((shop.get().isAdmin() && player.hasPermission(Permission.SHOP_ADMIN.toString())) || shop.get().isOwner(player.getUniqueId())) { + if((shop.get().isAdmin() && (player.hasPermission(Permission.SHOP_ADMIN.toString())) || shop.get().isOwner(player.getUniqueId()))) { InvAdminShop inv = new InvAdminShop(shop.get(), player); inv.open(player, shop.get().getOwner()); } else { @@ -719,12 +1588,28 @@ private void manageStock(Player player, String stockOwner, String page) { player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); return; } else { - try { - sOwner = getUUID(stockOwner); - } catch (Exception e) { - player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); - Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); - return; + Player playerInGame = Bukkit.getPlayer(stockOwner); + if(playerInGame != null && playerInGame.isOnline()) + sOwner = playerInGame.getUniqueId(); + else { + try { + sOwner = getUUID(stockOwner); + } catch (Exception e) { + UUID foundPlayerUUID = null; + boolean foundPlayer = false; + for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(stockOwner)) { + foundPlayerUUID = offlinePlayers.getUniqueId(); + foundPlayer = true; + break; + } + if(!foundPlayer) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + return; + } + sOwner = foundPlayerUUID; + } } } OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(sOwner); @@ -740,17 +1625,24 @@ private void manageStock(Player player, String stockOwner, String page) { return; } pageNum--; - int stockPages = iShop.config.getInt("stockPages"); - if(pageNum > 0 && pageNum > stockPages-1) - pageNum = stockPages-1; + int maxStockPages; + if(InvAdminShop.usePerms) + maxStockPages = InvAdminShop.permissionMax; + else + maxStockPages = InvAdminShop.maxPages; + if(pageNum > 0 && pageNum > maxStockPages-1) + pageNum = maxStockPages-1; final int openPage = pageNum; + final int stockPage = maxStockPages; + final UUID shopOwner = sOwner; Bukkit.getScheduler().runTask(iShop.getPlugin(), () -> { - if(InvStock.inShopInv.containsValue(sOwner)) { + if(InvStock.inShopInv.containsValue(shopOwner)) { player.sendMessage(Messages.SHOP_BUSY.toString()); return; } else - InvStock.inShopInv.put(player, sOwner); - InvStock inv = InvStock.getInvStock(sOwner); + InvStock.inShopInv.put(player, shopOwner); + InvStock inv = InvStock.getInvStock(shopOwner); + inv.setMaxPages(stockPage); inv.setPag(openPage); inv.open(player); }); @@ -864,15 +1756,98 @@ private void outOfStock(Player player, String playerName) { sOwner = player.getUniqueId(); playerName = player.getDisplayName(); } else { - try { - sOwner = getUUID(playerName); - } catch (Exception e) { - player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); - Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); - return; + Player playerInGame = Bukkit.getPlayer(playerName); + if(playerInGame != null && playerInGame.isOnline()) + sOwner = playerInGame.getUniqueId(); + else { + try { + sOwner = getUUID(playerName); + } catch (Exception e) { + UUID foundPlayerUUID = null; + boolean foundPlayer = false; + for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(playerName)) { + foundPlayerUUID = offlinePlayers.getUniqueId(); + foundPlayer = true; + break; + } + if(!foundPlayer) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + return; + } + sOwner = foundPlayerUUID; + } } } player.sendMessage(Messages.SHOP_LIST_OUT.toString()); Shop.getOutOfStock(player, sOwner, playerName); } + + private void purgePlayerStock(Player player, String stockOwner) { + if(!player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + UUID sOwner; + if(stockOwner == null) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + return; + } else { + Player playerInGame = Bukkit.getPlayer(stockOwner); + if(playerInGame != null && playerInGame.isOnline()) + sOwner = playerInGame.getUniqueId(); + else { + try { + sOwner = getUUID(stockOwner); + } catch (Exception e) { + UUID foundPlayerUUID = null; + boolean foundPlayer = false; + for(OfflinePlayer offlinePlayers : Bukkit.getOfflinePlayers()) + if(Objects.requireNonNull(offlinePlayers.getName()).equalsIgnoreCase(stockOwner)) { + foundPlayerUUID = offlinePlayers.getUniqueId(); + foundPlayer = true; + break; + } + if(!foundPlayer) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] " + Messages.NO_PLAYER_FOUND); + return; + } + sOwner = foundPlayerUUID; + } + } + } + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(sOwner); + if(offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) { + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + return; + } + StockShop.purgePlayerStock(sOwner); + String playerName = offlinePlayer.getName(); + if(playerName != null) + player.sendMessage(Messages.PURGE_PLAYER_STOCK.toString().replaceAll("%p", playerName)); + else + player.sendMessage(Messages.NO_PLAYER_FOUND.toString()); + } + + private void lockdownToggle(Player player) { + if(player != null && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { + player.sendMessage(Messages.NO_PERMISSION.toString()); + return; + } + if(iShop.getPlugin().checkLockdown()) { + iShop.getPlugin().setLockdown(false); + if(player != null) + player.sendMessage(Messages.STOCK_DISABLE_LOCKDOWN.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.STOCK_DISABLE_LOCKDOWN.toString()); + } else { + iShop.getPlugin().setLockdown(true); + if(player != null) + player.sendMessage(Messages.STOCK_ENABLE_LOCKDOWN.toString()); + else + Bukkit.getConsoleSender().sendMessage(Messages.STOCK_ENABLE_LOCKDOWN.toString()); + } + } } diff --git a/src/com/minedhype/ishop/EventShop.java b/src/com/minedhype/ishop/EventShop.java index ceb2a7b..b2ffff0 100644 --- a/src/com/minedhype/ishop/EventShop.java +++ b/src/com/minedhype/ishop/EventShop.java @@ -41,6 +41,7 @@ public class EventShop implements Listener { public static boolean noShopNoStock = iShop.config.getBoolean("mustOwnShopForStock"); public static boolean placeFrameSign = iShop.config.getBoolean("placeItemFrameSigns"); public static boolean protectShopFromExplosion = iShop.config.getBoolean("protectShopBlocksFromExplosions"); + public static boolean shuttingDown = false; public static int soldMessageDelayTime = iShop.config.getInt("soldNotificationsDelayTime"); public static int stockRangeLimit = iShop.config.getInt("stockRangeLimitFromShop"); public static String shopBlock = iShop.config.getString("shopBlock"); @@ -82,12 +83,17 @@ public void onPlayerInteract(PlayerInteractEvent event) { if(placeFrameSign) if(event.getPlayer().isSneaking() && event.getAction().equals(Action.RIGHT_CLICK_BLOCK) && (shop.get().isOwner(event.getPlayer().getUniqueId()) || event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) && (event.getPlayer().getInventory().getItemInMainHand().getType().toString().endsWith("_SIGN") || event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.ITEM_FRAME))) return; + event.setCancelled(true); + if(iShop.getPlugin().checkLockdown() && !event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) { + event.getPlayer().sendMessage(Messages.SHOP_LOCKDOWN.toString()); + return; + } if(shop.get().isAdmin() && !adminShopEnabled) { - event.setCancelled(true); event.getPlayer().sendMessage(Messages.ADMIN_SHOP_DISABLED.toString()); return; } - event.setCancelled(true); + if(shuttingDown) + return; if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { if(event.getHand().equals(EquipmentSlot.HAND)) event.getPlayer().sendMessage(Messages.SHOP_BUSY.toString()); @@ -116,12 +122,17 @@ else if(event.getHand().equals(EquipmentSlot.OFF_HAND)) if(placeFrameSign) if(event.getPlayer().isSneaking() && event.getAction().equals(Action.RIGHT_CLICK_BLOCK) && (shop.get().isOwner(event.getPlayer().getUniqueId()) || event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) && (event.getPlayer().getInventory().getItemInMainHand().getType().toString().endsWith("_SIGN") || event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.ITEM_FRAME))) return; + event.setCancelled(true); + if(iShop.getPlugin().checkLockdown() && !event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) { + event.getPlayer().sendMessage(Messages.SHOP_LOCKDOWN.toString()); + return; + } if(shop.get().isAdmin() && !adminShopEnabled) { - event.setCancelled(true); event.getPlayer().sendMessage(Messages.ADMIN_SHOP_DISABLED.toString()); return; } - event.setCancelled(true); + if(shuttingDown) + return; if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { if(event.getHand().equals(EquipmentSlot.HAND)) event.getPlayer().sendMessage(Messages.SHOP_BUSY.toString()); @@ -153,12 +164,17 @@ else if(event.getHand().equals(EquipmentSlot.OFF_HAND)) if(placeFrameSign) if(event.getPlayer().isSneaking() && event.getAction().equals(Action.RIGHT_CLICK_BLOCK) && (shop.get().isOwner(event.getPlayer().getUniqueId()) || event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) && (event.getPlayer().getInventory().getItemInMainHand().getType().toString().endsWith("_SIGN") || event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.ITEM_FRAME))) return; + event.setCancelled(true); + if(iShop.getPlugin().checkLockdown() && !event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) { + event.getPlayer().sendMessage(Messages.SHOP_LOCKDOWN.toString()); + return; + } if(shop.get().isAdmin() && !adminShopEnabled) { - event.setCancelled(true); event.getPlayer().sendMessage(Messages.ADMIN_SHOP_DISABLED.toString()); return; } - event.setCancelled(true); + if(shuttingDown) + return; if(InvStock.inShopInv.containsValue(shop.get().getOwner())) { if(event.getHand().equals(EquipmentSlot.HAND)) event.getPlayer().sendMessage(Messages.SHOP_BUSY.toString()); @@ -186,7 +202,13 @@ else if(event.getHand().equals(EquipmentSlot.OFF_HAND)) if(!isShopLoc || event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking()) return; event.setCancelled(true); + if(shuttingDown) + return; if(event.getAction() == Action.LEFT_CLICK_BLOCK) { + if(iShop.getPlugin().checkLockdown() && !event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) { + event.getPlayer().sendMessage(Messages.SHOP_LOCKDOWN.toString()); + return; + } if(Shop.getNumShops(event.getPlayer().getUniqueId()) < 1 && noShopNoStock) { event.getPlayer().sendMessage(Messages.NO_SHOP_STOCK.toString()); return; @@ -201,6 +223,21 @@ else if(event.getHand().equals(EquipmentSlot.OFF_HAND)) return; } else { InvStock.inShopInv.put(event.getPlayer(), event.getPlayer().getUniqueId()); } InvStock inv = InvStock.getInvStock(event.getPlayer().getUniqueId()); + int maxStockPages = InvAdminShop.maxPages; + if(InvAdminShop.usePerms) { + String permPrefix = Permission.SHOP_STOCK_PREFIX.toString(); + int maxPermPages = InvAdminShop.permissionMax; + boolean permissionFound = false; + for(int i=maxPermPages; i>0; i--) + if(event.getPlayer().hasPermission(permPrefix + i)) { + maxStockPages = i; + permissionFound = true; + break; + } + if(!permissionFound) + maxStockPages = maxPermPages; + } + inv.setMaxPages(maxStockPages); inv.setPag(0); inv.open(event.getPlayer()); } @@ -218,7 +255,13 @@ else if(event.getHand().equals(EquipmentSlot.OFF_HAND)) if(!isShopLoc || event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking()) return; event.setCancelled(true); + if(shuttingDown) + return; if(event.getAction() == Action.LEFT_CLICK_BLOCK) { + if(iShop.getPlugin().checkLockdown() && !event.getPlayer().hasPermission(Permission.SHOP_ADMIN.toString())) { + event.getPlayer().sendMessage(Messages.SHOP_LOCKDOWN.toString()); + return; + } if(Shop.getNumShops(event.getPlayer().getUniqueId()) < 1 && noShopNoStock) { event.getPlayer().sendMessage(Messages.NO_SHOP_STOCK.toString()); return; @@ -233,6 +276,21 @@ else if(event.getHand().equals(EquipmentSlot.OFF_HAND)) return; } else { InvStock.inShopInv.put(event.getPlayer(), event.getPlayer().getUniqueId()); } InvStock inv = InvStock.getInvStock(event.getPlayer().getUniqueId()); + int maxStockPages = InvAdminShop.maxPages; + if(InvAdminShop.usePerms) { + String permPrefix = Permission.SHOP_STOCK_PREFIX.toString(); + int maxPermPages = InvAdminShop.permissionMax; + boolean permissionFound = false; + for(int i=maxPermPages; i>0; i--) + if(event.getPlayer().hasPermission(permPrefix + i)) { + maxStockPages = i; + permissionFound = true; + break; + } + if(!permissionFound) + maxStockPages = maxPermPages; + } + inv.setMaxPages(maxStockPages); inv.setPag(0); inv.open(event.getPlayer()); } diff --git a/src/com/minedhype/ishop/Messages.java b/src/com/minedhype/ishop/Messages.java index 41641fa..203bf22 100644 --- a/src/com/minedhype/ishop/Messages.java +++ b/src/com/minedhype/ishop/Messages.java @@ -7,7 +7,9 @@ public enum Messages { ADMIN_SHOP_NUMBER("adminShopNumber"), DISABLED_SHOP_BLOCK("disabledShopBlock"), EXISTING_SHOP("existingShop"), + GP_CLAIM("noClaimPermission"), NOT_A_PLAYER("notPlayer"), + NO_SHOP_CREATE_PERMISSION("noPermissionToCreateShop"), NO_PERMISSION("noPermissions"), NO_PLAYER_FOUND("noPlayerFound"), NO_PLAYER_SHOP("noPlayerShop"), @@ -16,13 +18,20 @@ public enum Messages { NO_STOCK_BLOCK("noStockBlock"), PLAYER_INV_FULL("playerInventoryFull"), PLAYER_SHOP_CREATED("playerShopCreated"), + PURGE_PLAYER_STOCK("purgePlayersStock"), SHOP_BUSY("shopBusy"), SHOP_CLICK_MANAGE("clickManage"), SHOP_CLICK_SHOP("clickShop"), SHOP_CREATED("shopCreated"), + SHOP_CREATED_LOC("shopCreatedLoc"), SHOP_CREATE_NO_MONEY("noMoney"), SHOP_DELETED("shopDeleted"), + SHOP_DELETED_LOC("shopDeletedLoc"), SHOP_FAR("outOfStockRange"), + SHOP_FIND("listFind"), + SHOP_FIND_COOLDOWN("cooldownMessageFind"), + SHOP_FIND_ERROR("findError"), + SHOP_FIND_NONE("nolistFind"), SHOP_FOUND("foundShops"), SHOP_IDDELETED("shopIDDeleted"), SHOP_ID_INTEGER("shopIntegerError"), @@ -31,7 +40,11 @@ public enum Messages { SHOP_LIST_DISABLED("shopListDisabled"), SHOP_LIST_OUT("listOutOfStock"), SHOP_LOCATION("location"), + SHOP_LOCATION_ERROR_NUM("shopLocationErrorNum"), + SHOP_LOCATION_ERROR_WORLD("shopLocationErrorWorld"), + SHOP_LOCKDOWN("lockdown"), SHOP_MAX("shopLimit"), + SHOP_MOVED("shopMoved"), SHOP_NOT_FOUND("noShopFound"), SHOP_NOT_OUT("notOutOfStock"), SHOP_NO_ADMINSHOPS_FOUND("noAdminShopsFound"), @@ -44,9 +57,12 @@ public enum Messages { SHOP_NUMBER("shopNumber"), SHOP_OUT("outOfStock"), SHOP_PAGE("page"), + SHOP_PAGE_SKIPAHEAD("pageSkipAhead"), + SHOP_PAGE_SKIPPREV("pageSkipPrev"), SHOP_PURCHASE("buy"), SHOP_RELOAD("reload"), SHOP_REMOTE("noRemoteManage"), + SHOP_REMOVED_ALL_PLAYER("removedAllPlayer"), SHOP_SELL("sell"), SHOP_TITLE_ADMIN_SHOP("adminShop"), SHOP_TITLE_BROADCAST_OFF("broadcastOff"), @@ -75,6 +91,8 @@ public enum Messages { STOCK_COUNT_AMOUNT("countAmount"), STOCK_COUNT_EMPTY("countEmpty"), STOCK_COUNT_ERROR("countError"), + STOCK_DISABLE_LOCKDOWN("disableLockdown"), + STOCK_ENABLE_LOCKDOWN("enableLockdown"), STOCK_INTEGER("stockIntegerError"), TARGET_MISMATCH("targetMismatch"), WG_REGION("outsideRegion"); diff --git a/src/com/minedhype/ishop/Metrics.java b/src/com/minedhype/ishop/Metrics.java new file mode 100644 index 0000000..2721af4 --- /dev/null +++ b/src/com/minedhype/ishop/Metrics.java @@ -0,0 +1,905 @@ +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ +package com.minedhype.ishop; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; +import javax.net.ssl.HttpsURLConnection; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class Metrics { + + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + public Metrics(Plugin plugin, int serviceId) { + this.plugin = plugin; + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.\n" + + "Learn more here: https://bstats.org/docs/server-owners") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + boolean isFolia = false; + try { + isFolia = Class.forName("io.papermc.paper.threadedregions.RegionizedServer") != null; + } catch (Exception e) { + } + metricsBase = + new // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + // See https://github.com/Bastian/bstats-metrics/pull/126 + MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + isFolia + ? null + : submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText, + false); + } + + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); + } + } + + public static class MetricsBase { + + /** The version of the Metrics class. */ + public static final String METRICS_VERSION = "3.2.1"; + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final ScheduledExecutorService scheduler; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. + * + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. + * @param skipRelocateCheck Whether or not the relocate check should be skipped. + */ + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText, + boolean skipRelocateCheck) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor( + 1, + task -> { + Thread thread = new Thread(task, "bStats-Metrics"); + thread.setDaemon(true); + return thread; + }); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + if (!skipRelocateCheck) { + checkRelocation(); + } + if (enabled) { + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats + startSubmitting(); + } + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + public void shutdown() { + scheduler.shutdown(); + } + + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** Checks that the class was properly relocated. */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D + final String defaultPackage = + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + /** + * Gzips the given string. + * + * @param str The string to gzip. + * @return The gzipped string. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + } + + public abstract static class CustomChart { + + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; + } + + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } + + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } + + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); + + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); + } + + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } + + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } + + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } + + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } + + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } + + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. + */ + public static class JsonObject { + + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + } +} diff --git a/src/com/minedhype/ishop/MetricsLite.java b/src/com/minedhype/ishop/MetricsLite.java deleted file mode 100644 index e3ecb1d..0000000 --- a/src/com/minedhype/ishop/MetricsLite.java +++ /dev/null @@ -1,367 +0,0 @@ -package com.minedhype.ishop; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.plugin.ServicePriority; - -import javax.net.ssl.HttpsURLConnection; -import java.io.*; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Timer; -import java.util.TimerTask; -import java.util.UUID; -import java.util.logging.Level; -import java.util.zip.GZIPOutputStream; - -/** - * bStats collects some data for plugin authors. - *

- * Check out https://bStats.org/ to learn more about bStats! - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -public class MetricsLite { - - static { - // You can use the property to disable the check in your test environment - if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D - final String defaultPackage = new String( - new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); - final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure nobody just copy & pastes the example and use the wrong package names - if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } - } - - // The version of this bStats class - public static final int B_STATS_VERSION = 1; - - // The url to which the data is sent - private static final String URL = "https://bStats.org/submitData/bukkit"; - - // Is bStats enabled on this server? - private boolean enabled; - - // Should failed requests be logged? - private static boolean logFailedRequests; - - // Should the sent data be logged? - private static boolean logSentData; - - // Should the response text be logged? - private static boolean logResponseStatusText; - - // The uuid of the server - private static String serverUUID; - - // The plugin - private final Plugin plugin; - - // The plugin id - private final int pluginId; - - /** - * Class constructor. - * - * @param plugin The plugin which stats should be submitted. - * @param pluginId The id of the plugin. - * It can be found at What is my plugin id? - */ - public MetricsLite(Plugin plugin, int pluginId) { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null!"); - } - this.plugin = plugin; - this.pluginId = pluginId; - - // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); - File configFile = new File(bStatsFolder, "config.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - // Check if the config file exists - if (!config.isSet("serverUuid")) { - - // Add default values - config.addDefault("enabled", true); - // Every server gets it's unique random id. - config.addDefault("serverUuid", UUID.randomUUID().toString()); - // Should failed request be logged? - config.addDefault("logFailedRequests", false); - // Should the sent data be logged? - config.addDefault("logSentData", false); - // Should the response text be logged? - config.addDefault("logResponseStatusText", false); - - // Inform the server owners about bStats - config.options().header( - "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + - "To honor their work, you should not disable it.\n" + - "This has nearly no effect on the server performance!\n" + - "Check out https://bStats.org/ to learn more :)" - ).copyDefaults(true); - try { - config.save(configFile); - } catch (IOException ignored) { } - } - - // Load the data - serverUUID = config.getString("serverUuid"); - logFailedRequests = config.getBoolean("logFailedRequests", false); - enabled = config.getBoolean("enabled", true); - logSentData = config.getBoolean("logSentData", false); - logResponseStatusText = config.getBoolean("logResponseStatusText", false); - if (enabled) { - boolean found = false; - // Search for all other bStats Metrics classes to see if we are the first one - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - found = true; // We aren't the first - break; - } catch (NoSuchFieldException ignored) { } - } - // Register our service - Bukkit.getServicesManager().register(MetricsLite.class, this, plugin, ServicePriority.Normal); - if (!found) { - // We are the first! - startSubmitting(); - } - } - } - - /** - * Checks if bStats is enabled. - * - * @return Whether bStats is enabled or not. - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { - final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags - timer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - if (!plugin.isEnabled()) { // Plugin was disabled - timer.cancel(); - return; - } - // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler - // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) - Bukkit.getScheduler().runTask(plugin, () -> submitData()); - } - }, 1000 * 60 * 5, 1000 * 60 * 30); - // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start - // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! - // WARNING: Just don't do it! - } - - /** - * Gets the plugin specific data. - * This method is called using Reflection. - * - * @return The plugin specific data. - */ - public JsonObject getPluginData() { - JsonObject data = new JsonObject(); - - String pluginName = plugin.getDescription().getName(); - String pluginVersion = plugin.getDescription().getVersion(); - - data.addProperty("pluginName", pluginName); // Append the name of the plugin - data.addProperty("id", pluginId); // Append the id of the plugin - data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin - data.add("customCharts", new JsonArray()); - - return data; - } - - /** - * Gets the server specific data. - * - * @return The server specific data. - */ - private JsonObject getServerData() { - // Minecraft specific data - int playerAmount; - try { - // Around MC 1.8 the return type was changed to a collection from an array, - // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) - ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() - : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed - } - int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; - String bukkitVersion = Bukkit.getVersion(); - String bukkitName = Bukkit.getName(); - - // OS/Java specific data - String javaVersion = System.getProperty("java.version"); - String osName = System.getProperty("os.name"); - String osArch = System.getProperty("os.arch"); - String osVersion = System.getProperty("os.version"); - int coreCount = Runtime.getRuntime().availableProcessors(); - - JsonObject data = new JsonObject(); - - data.addProperty("serverUUID", serverUUID); - - data.addProperty("playerAmount", playerAmount); - data.addProperty("onlineMode", onlineMode); - data.addProperty("bukkitVersion", bukkitVersion); - data.addProperty("bukkitName", bukkitName); - - data.addProperty("javaVersion", javaVersion); - data.addProperty("osName", osName); - data.addProperty("osArch", osArch); - data.addProperty("osVersion", osVersion); - data.addProperty("coreCount", coreCount); - - return data; - } - - /** - * Collects the data and sends it afterwards. - */ - private void submitData() { - final JsonObject data = getServerData(); - - JsonArray pluginData = new JsonArray(); - // Search for all other bStats Metrics classes to get their plugin data - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - - for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { - try { - Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider()); - if (plugin instanceof JsonObject) { - pluginData.add((JsonObject) plugin); - } else { // old bstats version compatibility - try { - Class jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject"); - if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) { - Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString"); - jsonStringGetter.setAccessible(true); - String jsonString = (String) jsonStringGetter.invoke(plugin); - JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject(); - pluginData.add(object); - } - } catch (ClassNotFoundException e) { - // minecraft version 1.14+ - if (logFailedRequests) { - this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e); - } - } - } - } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { - } - } - } catch (NoSuchFieldException ignored) { } - } - - data.add("plugins", pluginData); - - // Create a new thread for the connection to the bStats server - new Thread(() -> { - try { - // Send the data - sendData(plugin, data); - } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); - } - } - }).start(); - } - - /** - * Sends the data to the bStats server. - * - * @param plugin Any plugin. It's just used to get a logger instance. - * @param data The data to send. - * @throws Exception If the request failed. - */ - private static void sendData(Plugin plugin, JsonObject data) throws Exception { - if (data == null) { - throw new IllegalArgumentException("Data cannot be null!"); - } - if (Bukkit.isPrimaryThread()) { - throw new IllegalAccessException("This method must not be called from the main thread!"); - } - if (logSentData) { - plugin.getLogger().info("Sending data to bStats: " + data); - } - HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); - - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - - // Add headers - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format - connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); - - // Send data - connection.setDoOutput(true); - try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { - outputStream.write(compressedData); - } - - StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - builder.append(line); - } - } - - if (logResponseStatusText) { - plugin.getLogger().info("Sent data to bStats and received response: " + builder); - } - } - - /** - * Gzips the given String. - * - * @param str The string to gzip. - * @return The gzipped String. - * @throws IOException If the compression failed. - */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try(GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { - gzip.write(str.getBytes(StandardCharsets.UTF_8)); - } - return outputStream.toByteArray(); - } - -} diff --git a/src/com/minedhype/ishop/Permission.java b/src/com/minedhype/ishop/Permission.java index 1130828..0bc9d78 100644 --- a/src/com/minedhype/ishop/Permission.java +++ b/src/com/minedhype/ishop/Permission.java @@ -2,10 +2,18 @@ public enum Permission { SHOP_ADMIN("ishop.admin"), + SHOP_BYPASS_FIND_CD("ishop.bypassfindcooldown"), SHOP_CREATE("ishop.create"), + SHOP_FIND("ishop.find"), SHOP_LIMIT_BYPASS("ishop.create.limit.bypass"), SHOP_LIMIT_PREFIX("ishop.create.limit."), - SHOP_STOCK("ishop.stock"); + SHOP_LIST("ishop.list"), + SHOP_MOVE("ishop.move"), + SHOP_REMOTEMANAGE("ishop.remotemanage"), + SHOP_REMOTESHOPPING("ishop.remoteshopping"), + SHOP_SHOPS("ishop.shops"), + SHOP_STOCK("ishop.stock"), + SHOP_STOCK_PREFIX("ishop.pages."); private final String perm; Permission(String perms) { diff --git a/src/com/minedhype/ishop/RowStore.java b/src/com/minedhype/ishop/RowStore.java index c2cc624..3cea03a 100644 --- a/src/com/minedhype/ishop/RowStore.java +++ b/src/com/minedhype/ishop/RowStore.java @@ -1,8 +1,9 @@ package com.minedhype.ishop; import java.sql.PreparedStatement; +import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; public class RowStore { @@ -24,47 +25,36 @@ public RowStore(ItemStack itemOut, ItemStack itemOut2, ItemStack itemIn, ItemSta public void saveData(int idTienda) { PreparedStatement stmt = null; try { - stmt = iShop.getConnection().prepareStatement("INSERT INTO zooMercaTiendasFilas (itemIn, itemIn2, itemOut, itemOut2, idTienda, broadcast) VALUES (?,?,?,?,?,?);"); - YamlConfiguration configIn1 = new YamlConfiguration(); + stmt = iShop.getConnection().prepareStatement("INSERT INTO zooMercaTiendasFilas (itemInNew, itemIn2New, itemOutNew, itemOut2New, idTienda, broadcast) VALUES (?,?,?,?,?,?);"); + ItemStack[] saveAirItem = new ItemStack[]{airItem}; + final Inventory invIn = Bukkit.createInventory(null,9); + final Inventory invIn2 = Bukkit.createInventory(null,9); + final Inventory invOut = Bukkit.createInventory(null,9); + final Inventory invOut2 = Bukkit.createInventory(null,9); if(itemIn != null) { - itemIn.serialize().forEach(configIn1::set); - String itemInRaw = configIn1.saveToString(); - stmt.setString(1, itemInRaw); - } else { - airItem.serialize().forEach(configIn1::set); - String itemInRaw = configIn1.saveToString(); - stmt.setString(1, itemInRaw); - } - YamlConfiguration configIn2 = new YamlConfiguration(); + ItemStack[] item = new ItemStack[]{itemIn}; + invIn.addItem(item[0]); + } else + invIn.addItem(saveAirItem[0]); + stmt.setBytes(1, iShop.encodeByte(invIn.getContents())); if(itemIn2 != null) { - itemIn2.serialize().forEach(configIn2::set); - String itemIn2Raw = configIn2.saveToString(); - stmt.setString(2, itemIn2Raw); - } else { - airItem.serialize().forEach(configIn2::set); - String itemIn2Raw = configIn2.saveToString(); - stmt.setString(2, itemIn2Raw); - } - YamlConfiguration configOut1 = new YamlConfiguration(); + ItemStack[] item = new ItemStack[]{itemIn2}; + invIn2.addItem(item[0]); + } else + invIn2.addItem(saveAirItem[0]); + stmt.setBytes(2, iShop.encodeByte(invIn2.getContents())); if(itemOut != null) { - itemOut.serialize().forEach(configOut1::set); - String itemOutRaw = configOut1.saveToString(); - stmt.setString(3, itemOutRaw); - } else { - airItem.serialize().forEach(configOut1::set); - String itemOutRaw = configOut1.saveToString(); - stmt.setString(3, itemOutRaw); - } - YamlConfiguration configOut2 = new YamlConfiguration(); + ItemStack[] item = new ItemStack[]{itemOut}; + invOut.addItem(item[0]); + } else + invOut.addItem(saveAirItem[0]); + stmt.setBytes(3, iShop.encodeByte(invOut.getContents())); if(itemOut2 != null) { - itemOut2.serialize().forEach(configOut2::set); - String itemOut2Raw = configOut2.saveToString(); - stmt.setString(4, itemOut2Raw); - } else { - airItem.serialize().forEach(configOut2::set); - String itemOut2Raw = configOut2.saveToString(); - stmt.setString(4, itemOut2Raw); - } + ItemStack[] item = new ItemStack[]{itemOut2}; + invOut2.addItem(item[0]); + } else + invOut2.addItem(saveAirItem[0]); + stmt.setBytes(4, iShop.encodeByte(invOut2.getContents())); stmt.setInt(5, idTienda); stmt.setBoolean(6, broadcast); stmt.execute(); @@ -81,15 +71,23 @@ public void toggleBroadcast() { this.broadcast = !this.broadcast; } public ItemStack getItemIn() { + if(itemIn == null) + return airItem; return itemIn; } public ItemStack getItemIn2() { + if(itemIn2 == null) + return airItem; return itemIn2; } public ItemStack getItemOut() { + if(itemOut == null) + return airItem; return itemOut; } public ItemStack getItemOut2() { + if(itemOut2 == null) + return airItem; return itemOut2; } } diff --git a/src/com/minedhype/ishop/Shop.java b/src/com/minedhype/ishop/Shop.java index 9d8170a..d91d5ca 100644 --- a/src/com/minedhype/ishop/Shop.java +++ b/src/com/minedhype/ishop/Shop.java @@ -1,5 +1,6 @@ package com.minedhype.ishop; +import java.sql.Blob; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; @@ -10,7 +11,9 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import javax.sql.rowset.serial.SerialBlob; import com.minedhype.ishop.inventories.InvAdminShop; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -19,11 +22,14 @@ import org.bukkit.OfflinePlayer; import org.bukkit.Particle; import org.bukkit.World; +import org.bukkit.block.ShulkerBox; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.plugin.Plugin; import com.google.gson.JsonArray; @@ -33,17 +39,23 @@ import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ClickEvent.Action; import net.md_5.bungee.api.chat.TextComponent; +import net.milkbowl.vault.economy.Economy; +import static com.minedhype.ishop.iShop.decodeByte; +import static com.minedhype.ishop.StockShop.purgePlayerStock; public class Shop { public static boolean deletePlayerShop = iShop.config.getBoolean("deleteBlock"); public static boolean particleEffects = iShop.config.getBoolean("showParticles"); + public static boolean saveEmptyShops = iShop.config.getBoolean("saveEmptyShops"); public static boolean shopOutStock = iShop.config.getBoolean("enableOutOfStockMessages"); public static boolean shopEnabled = iShop.config.getBoolean("enableShopBlock"); public static boolean showOwnedShops = iShop.config.getBoolean("publicShopListShowsOwned"); public static boolean shopNotifications = iShop.config.getBoolean("enableShopNotifications"); public static boolean stockMessages = iShop.config.getBoolean("enableShopSoldMessage"); public static boolean stockMessagesSaveAll = iShop.config.getBoolean("enableSavingAllShopSoldMessages"); + public static boolean stockDeleteWhenShopsExpire = iShop.config.getBoolean("deleteStockWhenShopsExpire"); public static List exemptExpiringList = iShop.config.getStringList("exemptExpiringShops"); + public static Particle shopParticles = Particle.valueOf(iShop.config.getString("shopParticles").toUpperCase()); public static int maxDays = iShop.config.getInt("maxInactiveDays"); public static final ConcurrentHashMap shopList = new ConcurrentHashMap<>(); public static final ConcurrentHashMap> shopMessages = new ConcurrentHashMap<>(); @@ -96,6 +108,32 @@ private Shop(int idTienda, UUID owner, Location loc, boolean admin) { public static boolean checkShopDistanceFromStockBlock(Location stockLocation, UUID shopOwner) { return shops.parallelStream().filter(s -> !s.admin && s.isOwner(shopOwner)).anyMatch(s -> s.getLocation().getWorld().equals(stockLocation.getWorld()) && s.location.distanceSquared(stockLocation) <= EventShop.stockRangeLimit*EventShop.stockRangeLimit); } public static int getNumShops(UUID owner) { return (int) shops.parallelStream().filter(t -> !t.admin && t.owner.equals(owner)).count(); } + public static boolean strictStockShopCheck(ItemStack item, ItemStack item2, UUID uuid) { + AtomicBoolean restricted = new AtomicBoolean(true); + shops.parallelStream().filter(s -> !s.admin && s.isOwner(uuid)).forEach(s -> { + if(restricted.get()) { + for(int i=0; i<5; i++) { + if(restricted.get()) { + Optional row = s.getRow(i); + if(row.isPresent()) { + if(!item.getType().isAir()) + if(row.get().getItemOut().isSimilar(item) || row.get().getItemOut2().isSimilar(item)) { + restricted.set(false); + break; + } + if(!item2.getType().isAir()) + if(row.get().getItemOut().isSimilar(item2) || row.get().getItemOut2().isSimilar(item2)) { + restricted.set(false); + break; + } + } + } + } + } + }); + return restricted.get(); + } + public static void getPlayersShopList() { if(iShop.config.getBoolean("adminShopPublic")) shops.parallelStream().forEach(s -> shopList.putIfAbsent(s.idTienda, s.owner)); @@ -103,18 +141,204 @@ public static void getPlayersShopList() { shops.parallelStream().filter(s -> !s.admin).forEach(s -> shopList.putIfAbsent(s.idTienda, s.owner)); } + public static void findItem(Player player, ItemStack item, String itemName) { + player.sendMessage(Messages.SHOP_FIND.toString().replaceAll("%item", itemName.toLowerCase())); + AtomicBoolean foundItem = new AtomicBoolean(false); + shops.parallelStream().forEach(s -> { + AtomicBoolean foundItemInShop = new AtomicBoolean(false); + for(int i=0; i<5; i++) { + Optional row = s.getRow(i); + if(row.isPresent() && !foundItemInShop.get()) { + boolean itemMatch = row.get().getItemOut().isSimilar(item); + boolean itemMatch2 = row.get().getItemOut2().isSimilar(item); + if(((itemMatch && itemMatch2) && Utils.hasDoubleItemStock(s, row.get().getItemOut(), row.get().getItemOut2())) || ((itemMatch && !itemMatch2) && Utils.hasStock(s, row.get().getItemOut())) || ((!itemMatch && itemMatch2) && Utils.hasStock(s, row.get().getItemOut2()))) { + foundItem.set(true); + foundItemInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + if(row.get().getItemOut().getType().toString().contains("SHULKER_BOX") && row.get().getItemOut().getItemMeta() instanceof BlockStateMeta && Utils.hasStock(s, row.get().getItemOut())) { + BlockStateMeta itemMeta = (BlockStateMeta) row.get().getItemOut().getItemMeta(); + ShulkerBox shulkerBox = (ShulkerBox) itemMeta.getBlockState(); + if(shulkerBox.getInventory().contains(item.getType())) + for(ItemStack itemSearch:shulkerBox.getInventory().getContents()) { + if(itemSearch.isSimilar(item)) { + foundItem.set(true); + foundItemInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + } + if(row.get().getItemOut2().getType().toString().contains("SHULKER_BOX") && row.get().getItemOut2().getItemMeta() instanceof BlockStateMeta && Utils.hasStock(s, row.get().getItemOut2())) { + BlockStateMeta itemMeta = (BlockStateMeta) row.get().getItemOut2().getItemMeta(); + ShulkerBox shulkerBox = (ShulkerBox) itemMeta.getBlockState(); + if(shulkerBox.getInventory().contains(item.getType())) + for(ItemStack itemSearch:shulkerBox.getInventory().getContents()) { + if(itemSearch.isSimilar(item)) { + foundItem.set(true); + foundItemInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + } + } + } + }); + if(!foundItem.get()) + player.sendMessage(Messages.SHOP_FIND_NONE.toString()); + } + public static void findBook(Player player, String bookName) { + player.sendMessage(Messages.SHOP_FIND.toString().replaceAll("%item", bookName.toLowerCase())); + AtomicBoolean foundItem = new AtomicBoolean(false); + shops.parallelStream().forEach(s -> { + AtomicBoolean foundBookInShop = new AtomicBoolean(false); + for(int i=0; i<5; i++) { + Optional row = s.getRow(i); + if(!row.isPresent()) + continue; + if(!foundBookInShop.get() && (row.get().getItemOut().getType().equals(Material.ENCHANTED_BOOK) || row.get().getItemOut2().getType().equals(Material.ENCHANTED_BOOK))) { + if(row.get().getItemOut().equals(row.get().getItemOut2()) && row.get().getItemOut().getItemMeta() instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) row.get().getItemOut().getItemMeta(); + if(meta.getStoredEnchants().toString().contains(bookName)) { + if(!Utils.hasEnchantment(s, bookName, true)) + break; + foundItem.set(true); + foundBookInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + else { + if(row.get().getItemOut().getItemMeta() instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) row.get().getItemOut().getItemMeta(); + if(meta.getStoredEnchants().toString().contains(bookName)) { + if(!Utils.hasEnchantment(s, bookName, false) || !Utils.hasStock(s, row.get().getItemOut2())) + break; + foundItem.set(true); + foundBookInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + if(row.get().getItemOut2().getItemMeta() instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta meta2 = (EnchantmentStorageMeta) row.get().getItemOut2().getItemMeta(); + if(meta2.getStoredEnchants().toString().contains(bookName)) { + if(!Utils.hasEnchantment(s, bookName, false) || !Utils.hasStock(s, row.get().getItemOut())) + break; + foundItem.set(true); + foundBookInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + } + } + if(!foundBookInShop.get() && (row.get().getItemOut().getType().toString().contains("SHULKER_BOX") && row.get().getItemOut2().getType().toString().contains("SHULKER_BOX")) && (row.get().getItemOut().getItemMeta() instanceof BlockStateMeta && row.get().getItemOut2().getItemMeta() instanceof BlockStateMeta) && row.get().getItemOut().equals(row.get().getItemOut2())) { + BlockStateMeta itemMeta = (BlockStateMeta) row.get().getItemOut().getItemMeta(); + ShulkerBox shulkerBox = (ShulkerBox) itemMeta.getBlockState(); + if(shulkerBox.getInventory().contains(Material.ENCHANTED_BOOK)) { + for(ItemStack item:shulkerBox.getInventory().getContents()) { + if(item != null && item.getType().equals(Material.ENCHANTED_BOOK) && item.hasItemMeta() && item.getItemMeta() instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta(); + if(meta.getStoredEnchants().toString().contains(bookName)) { + if(!Utils.hasDoubleItemStock(s, row.get().getItemOut(), row.get().getItemOut2())) + break; + foundItem.set(true); + foundBookInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + } + } + } + else { + if(!foundBookInShop.get() && row.get().getItemOut().getType().toString().contains("SHULKER_BOX") && row.get().getItemOut().getItemMeta() instanceof BlockStateMeta) { + BlockStateMeta itemMeta = (BlockStateMeta) row.get().getItemOut().getItemMeta(); + ShulkerBox shulkerBox = (ShulkerBox) itemMeta.getBlockState(); + if(shulkerBox.getInventory().contains(Material.ENCHANTED_BOOK)) { + for(ItemStack item:shulkerBox.getInventory().getContents()) { + if(item != null && item.getType().equals(Material.ENCHANTED_BOOK) && item.hasItemMeta() && item.getItemMeta() instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta(); + if(meta.getStoredEnchants().toString().contains(bookName)) { + if(!Utils.hasStock(s, row.get().getItemOut())) + break; + foundItem.set(true); + foundBookInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + } + } + } + if(!foundBookInShop.get() && row.get().getItemOut2().getType().toString().contains("SHULKER_BOX") && row.get().getItemOut2().getItemMeta() instanceof BlockStateMeta) { + BlockStateMeta itemMeta = (BlockStateMeta) row.get().getItemOut2().getItemMeta(); + ShulkerBox shulkerBox = (ShulkerBox) itemMeta.getBlockState(); + if(shulkerBox.getInventory().contains(Material.ENCHANTED_BOOK)) { + for(ItemStack item:shulkerBox.getInventory().getContents()) { + if(item != null && item.getType().equals(Material.ENCHANTED_BOOK) && item.hasItemMeta() && item.getItemMeta() instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta(); + if(meta.getStoredEnchants().toString().contains(bookName)) { + if(!Utils.hasStock(s, row.get().getItemOut2())) + break; + foundItem.set(true); + foundBookInShop.set(true); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); + break; + } + } + } + } + } + } + } + }); + if(!foundItem.get()) + player.sendMessage(Messages.SHOP_FIND_NONE.toString()); + } + + public static void removeAllPlayersShops(Player player, UUID sOwner, String pOwner) { + int deletedCount = 0; + List deleteShops = new ArrayList<>(); + for(Shop shop:shops) + if(!shop.admin && shop.isOwner(sOwner)) { + deleteShops.add(shop); + deletedCount++; + } + for(Shop shop:deleteShops) { + shopList.remove(shop.shopId()); + shop.deleteShop(); + } + Optional economy = iShop.getEconomy(); + if(economy.isPresent()) { + double cost = iShop.config.getDouble("returnAmount"); + if(deletedCount > 0 && cost > 0) { + OfflinePlayer offPlayer = Bukkit.getOfflinePlayer(sOwner); + double totalCost = cost * deletedCount; + economy.get().depositPlayer(offPlayer, totalCost); + } + } + if(deletedCount > 0) + player.sendMessage(Messages.SHOP_REMOVED_ALL_PLAYER.toString().replaceAll("%p", pOwner).replaceAll("%shops", String.valueOf(deletedCount))); + else + player.sendMessage(Messages.SHOP_NOT_FOUND.toString()); + } + public static void getShopList(Player player, UUID sOwner, String pOwner) { player.sendMessage(Messages.SHOP_FOUND.toString().replaceAll("%p", pOwner).replaceAll("%shops", String.valueOf(getNumShops(sOwner)))); shops.parallelStream().filter(s -> !s.admin && s.isOwner(sOwner)).forEach(s -> { if(s.isOwner(player.getUniqueId()) && InvAdminShop.remoteManage) { - String manageMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)) + ChatColor.GREEN + s.location.getBlockX() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockY() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockZ() + ChatColor.GOLD + " in " + ChatColor.GREEN + s.location.getWorld().getName(); + String manageMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName()); TextComponent manageMsg = new TextComponent(manageMessage); TextComponent manageText = new TextComponent(ChatColor.DARK_GRAY + " [" + Messages.SHOP_CLICK_MANAGE + ChatColor.DARK_GRAY + "]"); manageText.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/shop manage " + s.idTienda)); manageMsg.addExtra(manageText); player.spigot().sendMessage(manageMsg); } else if(player.hasPermission(Permission.SHOP_ADMIN.toString())) { - String manageMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)) + ChatColor.GREEN + s.location.getBlockX() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockY() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockZ() + ChatColor.GOLD + " in " + ChatColor.GREEN + s.location.getWorld().getName(); + String manageMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName()); TextComponent manageMsg = new TextComponent(manageMessage); TextComponent manageText = new TextComponent(ChatColor.DARK_GRAY + " [" + Messages.SHOP_CLICK_MANAGE + ChatColor.DARK_GRAY + "]"); manageText.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/shop manage " + s.idTienda)); @@ -129,14 +353,14 @@ public static void getShopList(Player player, UUID sOwner, String pOwner) { } else player.spigot().sendMessage(manageMsg); } else if(iShop.config.getBoolean("remoteShopping") && !s.isOwner(player.getUniqueId())) { - String shopMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)) + ChatColor.GREEN + s.location.getBlockX() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockY() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockZ() + ChatColor.GOLD + " in " + ChatColor.GREEN + s.location.getWorld().getName(); + String shopMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName()); TextComponent shopMsg = new TextComponent(shopMessage); TextComponent shopText = new TextComponent(ChatColor.DARK_GRAY + " [" + Messages.SHOP_CLICK_SHOP + ChatColor.DARK_GRAY + "]"); shopText.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/shop view " + s.idTienda)); shopMsg.addExtra(shopText); player.spigot().sendMessage(shopMsg); } else - player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)) + ChatColor.GREEN + s.location.getBlockX() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockY() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockZ() + ChatColor.GOLD + " in " + ChatColor.GREEN + s.location.getWorld().getName()); + player.sendMessage(Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName())); }); } @@ -144,7 +368,7 @@ public static void getAdminShopList(Player player) { player.sendMessage(Messages.SHOP_LIST_ADMINSHOPS.toString()); AtomicInteger shopCount = new AtomicInteger(0); shops.parallelStream().filter(s -> s.admin).forEach(s -> { - String manageMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)) + ChatColor.GREEN + s.location.getBlockX() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockY() + ChatColor.GOLD + " / " + ChatColor.GREEN + s.location.getBlockZ() + ChatColor.GOLD + " in " + ChatColor.GREEN + s.location.getWorld().getName(); + String manageMessage = Messages.SHOP_LOCATION.toString().replaceAll("%id", String.valueOf(s.idTienda)).replaceAll("%x", String.valueOf(s.location.getBlockX())).replaceAll("%y", String.valueOf(s.location.getBlockY())).replaceAll("%z", String.valueOf(s.location.getBlockZ())).replaceAll("%world", s.location.getWorld().getName()); TextComponent manageMsg = new TextComponent(manageMessage); TextComponent manageText = new TextComponent(ChatColor.DARK_GRAY + " [" + Messages.SHOP_CLICK_MANAGE + ChatColor.DARK_GRAY + "]"); manageText.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/shop manage " + s.idTienda)); @@ -391,6 +615,20 @@ public static Shop createShop(Location loc, UUID owner, boolean admin) { return shop; } + public static Shop duplicateShop(Location loc, UUID owner, int shopId) { + Shop shop = new Shop(-1, owner, loc, false); + shops.add(shop); + Optional stockShop = StockShop.getStockShopByOwner(owner, 0); + if(!stockShop.isPresent()) + new StockShop(owner, 0); + Optional copyShop = Shop.getShopById(shopId); + for(int i=0;i<5;i++) { + if(copyShop.get().getRows()[i] != null) + shop.getRows()[i] = copyShop.get().getRows()[i]; + } + return shop; + } + public static void tickShops() { if(!shopEnabled) return; @@ -400,22 +638,42 @@ public static void tickShops() { double x = shop.location.getBlockX() + 0.5; double y = shop.location.getBlockY() + 1.25; double z = shop.location.getBlockZ() + 0.5; - shop.location.getWorld().spawnParticle(Particle.VILLAGER_HAPPY, x, y, z, 10, 0.1, 0.1, 0.1); + try { + shop.location.getWorld().spawnParticle(shopParticles, x, y, z, 10, 0.1, 0.1, 0.1); + } catch(Exception e) { + shop.location.getWorld().spawnParticle(Particle.HAPPY_VILLAGER, x, y, z, 10, 0.1, 0.1, 0.1); + } } } } public static void expiredShops() { exemptListInactive = exemptExpiringList.size() == 1 && exemptExpiringList.get(0).equals("00000000-0000-0000-0000-000000000000"); + int expiredCount = 0; List shopDelete = new ArrayList<>(); - for(Shop shop : shops) - if(shop.hasExpired() || shop.location.getWorld() == null) + List shopOwners = new ArrayList<>(); + for(Shop shop : shops) { + if(shop.hasExpired()) { + shopDelete.add(shop); + expiredCount++; + if(stockDeleteWhenShopsExpire) { + purgePlayerStock(shop.getOwner()); + if(!shopOwners.contains(shop.getOwner())) + shopOwners.add(shop.getOwner()); + } + } + else if(shop.location.getWorld() == null) shopDelete.add(shop); + } for(Shop shop : shopDelete) { shopList.remove(shop.shopId()); shop.deleteShop(); } + if(expiredCount > 0 && !stockDeleteWhenShopsExpire) + Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] " + expiredCount + " shops have expired."); + else if(!shopOwners.isEmpty()) + Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] " + expiredCount + " shops have expired. Stock has expired for " + shopOwners.size() + " players."); } public static void loadData() { @@ -424,8 +682,104 @@ public static void loadData() { PreparedStatement loadShops = null; try { - loadStocks = iShop.getConnection().prepareStatement("SELECT owner, items, pag FROM zooMercaStocks;"); + loadStocks = iShop.getConnection().prepareStatement("SELECT owner, itemsNew, pag FROM zooMercaStocks;"); ResultSet dataStocks = loadStocks.executeQuery(); + while(dataStocks.next()) { + String ownerRaw = dataStocks.getString(1); + UUID owner = UUID.fromString(ownerRaw); + int pag = dataStocks.getInt(3); + try { + Blob blob = new SerialBlob(dataStocks.getBytes(2)); + StockShop stock = new StockShop(owner, pag); + stock.getInventory().setContents(decodeByte(blob.getBinaryStream())); + } catch (Exception ignored) { } + } + loadShops = iShop.getConnection().prepareStatement("SELECT location, owner, itemInNew, itemIn2New, itemOutNew, itemOut2New, idTienda, admin, broadcast FROM zooMercaTiendasFilas LEFT JOIN zooMercaTiendas ON id = idTienda ORDER BY idTienda;"); + ResultSet dataStore = loadShops.executeQuery(); + while(dataStore.next()) { + if(dataStore.getString(1) == null) { + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Error: Skipped loading a shop with null location found in database! Make backups!"); + continue; + } + String[] locationRaw = dataStore.getString(1).split(";"); + int x = Integer.parseInt(locationRaw[0]); + int y = Integer.parseInt(locationRaw[1]); + int z = Integer.parseInt(locationRaw[2]); + World world = Bukkit.getWorld(locationRaw[3]); + if(world == null) { + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Error: Skipped loading a shop with null world found in database! Make backups!"); + continue; + } + Location location = new Location(world, x, y, z); + Optional shop = Shop.getShopByLocation(location); + if(!shop.isPresent()) { + String ownerRaw = dataStore.getString(2); + UUID owner = UUID.fromString(ownerRaw); + int idTienda = dataStore.getInt(7); + boolean admin = dataStore.getBoolean(8); + shops.add(new Shop(idTienda, owner, location, admin)); + shop = Shop.getShopByLocation(location); + } + RowStore[] rows = shop.get().getRows(); + int index = 0; + for(int len=rows.length; index= rows.length) + continue; + boolean broadcast = dataStore.getBoolean(9); + ItemStack airItem = new ItemStack(Material.AIR, 0); + final Inventory invIn = Bukkit.createInventory(null,9); + final Inventory invIn2 = Bukkit.createInventory(null,9); + final Inventory invOut = Bukkit.createInventory(null,9); + final Inventory invOut2 = Bukkit.createInventory(null,9); + try { + Blob blob = new SerialBlob(dataStore.getBytes(3)); + invIn.setContents(decodeByte(blob.getBinaryStream())); + } + catch(Exception e) { invIn.addItem(airItem); } + try { + Blob blob = new SerialBlob(dataStore.getBytes(4)); + invIn2.setContents(decodeByte(blob.getBinaryStream())); + } + catch(Exception e) { invIn2.addItem(airItem); } + try { + Blob blob = new SerialBlob(dataStore.getBytes(5)); + invOut.setContents(decodeByte(blob.getBinaryStream())); + } + catch(Exception e) { invOut.addItem(airItem); } + try { + Blob blob = new SerialBlob(dataStore.getBytes(6)); + invOut2.setContents(decodeByte(blob.getBinaryStream())); + } + catch(Exception e) { invOut2.addItem(airItem); } + rows[index] = new RowStore(invOut.getItem(0), invOut2.getItem(0), invIn.getItem(0), invIn2.getItem(0), broadcast); + } + } catch(Exception e) { + e.printStackTrace(); + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Failed to load database properly! Shutting down to prevent data corruption."); + Bukkit.shutdown(); + } finally { + try { + if(loadStocks != null) + loadStocks.close(); + if(loadRows != null) + loadRows.close(); + if(loadShops != null) + loadShops.close(); + } catch (Exception e) { e.printStackTrace(); } + } + } + + public static void convertData() { + boolean failedToLoad = false; + PreparedStatement loadOldShops = null; + PreparedStatement loadOldStock = null; + try { + loadOldStock = iShop.getConnection().prepareStatement("SELECT owner, items, pag FROM zooMercaStocks;"); + ResultSet dataStocks = loadOldStock.executeQuery(); while(dataStocks.next()) { String ownerRaw = dataStocks.getString(1); UUID owner = UUID.fromString(ownerRaw); @@ -445,16 +799,22 @@ public static void loadData() { StockShop stock = new StockShop(owner, pag); stock.getInventory().setContents(itemsList.toArray(new ItemStack[0])); } - loadShops = iShop.getConnection().prepareStatement("SELECT location, owner, itemIn, itemIn2, itemOut, itemOut2, idTienda, admin, broadcast FROM zooMercaTiendasFilas LEFT JOIN zooMercaTiendas ON id = idTienda ORDER BY idTienda;"); - ResultSet dataStore = loadShops.executeQuery(); + loadOldShops = iShop.getConnection().prepareStatement("SELECT location, owner, itemIn, itemIn2, itemOut, itemOut2, idTienda, admin, broadcast FROM zooMercaTiendasFilas LEFT JOIN zooMercaTiendas ON id = idTienda ORDER BY idTienda;"); + ResultSet dataStore = loadOldShops.executeQuery(); while(dataStore.next()) { + if(dataStore.getString(1) == null) { + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Error: Skipped loading a shop with null location found in database! Make backups!"); + continue; + } String[] locationRaw = dataStore.getString(1).split(";"); int x = Integer.parseInt(locationRaw[0]); int y = Integer.parseInt(locationRaw[1]); int z = Integer.parseInt(locationRaw[2]); World world = Bukkit.getWorld(locationRaw[3]); - if(world == null) + if(world == null) { + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Error: Skipped loading a shop with null world found in database! Make backups!"); continue; + } Location location = new Location(world, x, y, z); Optional shop = Shop.getShopByLocation(location); if(!shop.isPresent()) { @@ -469,7 +829,7 @@ public static void loadData() { int index = 0; for(int len=rows.length; index= rows.length) @@ -487,7 +847,7 @@ public static void loadData() { configIn2.loadFromString(itemInstack2Raw); configOut1.loadFromString(itemOutstack1Raw); configOut2.loadFromString(itemOutstack2Raw); - } catch (InvalidConfigurationException e) { e.printStackTrace(); } + } catch (Exception e) { e.printStackTrace(); } Map itemInRaw = configIn1.getValues(true); ItemStack itemIn = ItemStack.deserialize(itemInRaw); Map itemIn2Raw = configIn2.getValues(true); @@ -499,24 +859,28 @@ public static void loadData() { boolean broadcast = dataStore.getBoolean(9); rows[index] = new RowStore(itemOut, itemOut2, itemIn, itemIn2, broadcast); } - } catch(Exception e) { e.printStackTrace(); - Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Failed to load database properly! Shutting down to prevent data corruption."); - Bukkit.shutdown(); - } finally { + Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "[iShop] Failed to convert/load database, keep multiple back-ups for safety!"); + failedToLoad = true; + } finally{ try { - if(loadStocks != null) - loadStocks.close(); - if(loadRows != null) - loadRows.close(); - if(loadShops != null) - loadShops.close(); - } catch (Exception e) { e.printStackTrace(); } + if(loadOldShops != null) + loadOldShops.close(); + if(loadOldStock != null) + loadOldStock.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + if(!failedToLoad) { + iShop.dropOldColumns(); + Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] Successfully converted database!"); + Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] We STRONGLY recommend restarting your server now to prevent data corruption!"); } } - public static void saveData() { + public static void saveData(boolean shutDown) { StockShop.saveData(); PreparedStatement stmt = null; try { @@ -529,8 +893,18 @@ public static void saveData() { stmt.close(); } catch (Exception e) { e.printStackTrace(); } } - for(Shop shop : shops) - shop.saveDataShop(); + if(shutDown && saveEmptyShops) { + for(Shop shop : shops) + if(shop.saveDataShopEmpty()) { + ItemStack air = new ItemStack(Material.AIR, 0); + shop.getRows()[0] = new RowStore(air, air, air, air, false); + shop.saveEmptyShops(); + } + } + else { + for(Shop shop : shops) + shop.saveDataShop(); + } } public boolean hasItems() { @@ -546,6 +920,30 @@ private void saveDataShop() { row.saveData(idTienda); } + private boolean saveDataShopEmpty() { + boolean isEmpty = true; + for(RowStore row : rows) + if(row != null) { + row.saveData(idTienda); + if(isEmpty) + isEmpty = false; + } + return isEmpty; + } + + private void saveEmptyShops() { + RowStore row = rows[0]; + row.saveData(idTienda); + } + + public static void removeEmptyShopTrade() { + for(Shop shop : shops) { + Optional row = shop.getRow(0); + if(row.get().getItemIn().getType().isAir() && row.get().getItemIn2().getType().isAir() && row.get().getItemOut().getType().isAir() && row.get().getItemOut2().getType().isAir()) + shop.delete(0); + } + } + public void buy(Player player, int index) { Optional row = getRow(index); if(!row.isPresent()) @@ -724,7 +1122,11 @@ public boolean hasExpired() { public void giveItem(ItemStack item) { ItemStack copy = item.clone(); - int max = iShop.config.getInt("stockPages"); + int max; + if(InvAdminShop.usePerms) + max = InvAdminShop.permissionMax; + else + max = InvAdminShop.maxPages; for(int i=0; i stock = StockShop.getStockShopByOwner(this.owner, i); if(!stock.isPresent()) @@ -740,7 +1142,11 @@ public void giveItem(ItemStack item) { public void takeItem(ItemStack item) { ItemStack copy = item.clone(); - int max = iShop.config.getInt("stockPages"); + int max; + if(InvAdminShop.usePerms) + max = InvAdminShop.permissionMax; + else + max = InvAdminShop.maxPages; for(int i=0; i stock = StockShop.getStockShopByOwner(this.owner, i); if(!stock.isPresent()) @@ -754,7 +1160,7 @@ public void takeItem(ItemStack item) { InvStock.getInvStock(this.owner).refreshItems(); } - public void delete(Player player, int index) { + public void delete(int index) { rows[index] = null; } public void deleteShop() { @@ -788,6 +1194,34 @@ public void deleteShop(boolean removalOfArray) { }); } + public static void moveShop(Location oldLoc, Location newLoc, Player player, int shopID) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + PreparedStatement stmt = null; + try { + stmt = iShop.getConnection().prepareStatement("UPDATE zooMercaTiendas SET location = ? WHERE location = ?;"); + String oldLocationRaw = oldLoc.getBlockX()+";"+oldLoc.getBlockY()+";"+oldLoc.getBlockZ()+";"+oldLoc.getWorld().getName(); + String newLocationRaw = newLoc.getBlockX()+";"+newLoc.getBlockY()+";"+newLoc.getBlockZ()+";"+newLoc.getWorld().getName(); + stmt.setString(1, newLocationRaw); + stmt.setString(2, oldLocationRaw); + stmt.executeUpdate(); + Optional newShopLoc = Shop.getShopById(shopID); + newShopLoc.get().location.setX(newLoc.getX()); + newShopLoc.get().location.setY(newLoc.getY()); + newShopLoc.get().location.setZ(newLoc.getZ()); + newShopLoc.get().location.setWorld(newLoc.getWorld()); + } catch (Exception e) { e.printStackTrace(); } + finally { + try { + if(stmt != null) + stmt.close(); + } catch (Exception e) { e.printStackTrace(); } + } + player.sendMessage(Messages.SHOP_MOVED.toString().replaceAll("%id", String.valueOf(shopID))); + }); + if(deletePlayerShop) + Bukkit.getScheduler().runTask(iShop.getPlugin(), () -> oldLoc.getBlock().setType(Material.AIR)); + } + public void sendShopMessages(String i1, String i2, String o1, String o2, int inA1, int inA2, int outA1, int outA2, UUID shopOwner, Player player, boolean isAdminShop, boolean rowBroadcast) { OfflinePlayer ownerPlayer = Bukkit.getOfflinePlayer(shopOwner); if(!isAdminShop) { @@ -1069,6 +1503,57 @@ public void outOfStockItem(Player ownerPlayer, String itemString) { } } + public static boolean hasDuplicateTrades(ItemStack out1, ItemStack out2, ItemStack in1, ItemStack in2, int shopId) { + Optional shop = getShopById(shopId); + for(RowStore row:shop.get().getRows()) { + if(row != null) { + boolean row1, row2, row3, row4; + if(row.getItemIn().equals(in1) || (row.getItemIn().getType().isAir() && in1.getType().isAir())) + row1 = true; + else continue; + if(row.getItemIn2().equals(in2) || (row.getItemIn2().getType().isAir() && in2.getType().isAir())) + row2 = true; + else continue; + if(row.getItemOut().equals(out1) || (row.getItemOut().getType().isAir() && out1.getType().isAir())) + row3 = true; + else continue; + if(row.getItemOut2().equals(out2) || (row.getItemOut2().getType().isAir() && out2.getType().isAir())) + row4 = true; + else continue; + if(row1 && row2 && row3 && row4) + return true; + } + } + return false; + } + + public static boolean hasAnyDuplicateTrades(ItemStack out1, ItemStack out2, ItemStack in1, ItemStack in2, UUID shopOwner) { + for(Shop shop:shops) { + if(shop.isOwner(shopOwner) && !shop.isAdmin()) { + for(RowStore row:shop.getRows()) { + if(row != null) { + boolean row1, row2, row3, row4; + if(row.getItemIn().equals(in1) || (row.getItemIn().getType().isAir() && in1.getType().isAir())) + row1 = true; + else continue; + if(row.getItemIn2().equals(in2) || (row.getItemIn2().getType().isAir() && in2.getType().isAir())) + row2 = true; + else continue; + if(row.getItemOut().equals(out1) || (row.getItemOut().getType().isAir() && out1.getType().isAir())) + row3 = true; + else continue; + if(row.getItemOut2().equals(out2) || (row.getItemOut2().getType().isAir() && out2.getType().isAir())) + row4 = true; + else continue; + if(row1 && row2 && row3 && row4) + return true; + } + } + } + } + return false; + } + public Optional getRow(int index) { if(index < 0 || index > 4) return Optional.empty(); diff --git a/src/com/minedhype/ishop/StockShop.java b/src/com/minedhype/ishop/StockShop.java index 5a63e69..1e320fb 100644 --- a/src/com/minedhype/ishop/StockShop.java +++ b/src/com/minedhype/ishop/StockShop.java @@ -6,20 +6,26 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.OfflinePlayer; import org.bukkit.inventory.Inventory; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import org.bukkit.inventory.ItemStack; public class StockShop { private static final List stocks = new ArrayList<>(); private final UUID owner; private final Inventory inventory; private final int pag; - + private static boolean exemptStockListInactive; + private static final long millisecondsPerDay = 86400000; + public boolean isOwner(UUID owner) { return this.owner.equals(owner); } + public static List expiredStockOwners = new ArrayList<>(); + public static List exemptStockExpiringList = iShop.config.getStringList("exemptExpiringStock"); + public static Optional getStockShopByOwner(UUID owner, int pag) { return stocks.parallelStream().filter(t -> t.owner.equals(owner) && t.pag == pag).findFirst(); } + public static final ConcurrentHashMap stockList = new ConcurrentHashMap<>(); + public static int maxStockDays = iShop.config.getInt("maxStockInactiveDays"); + public static void getPlayersStockList() { stocks.parallelStream().filter(s -> !s.inventory.isEmpty()).forEach(s -> stockList.putIfAbsent(s.owner, "Owner")); } public StockShop(UUID owner, int pag) { this(owner, Bukkit.createInventory(null, 45, ChatColor.GREEN + Bukkit.getOfflinePlayer(owner).getName()+"'s shop"), pag); } public StockShop(UUID owner, Inventory inv, int pag) { @@ -29,7 +35,73 @@ public StockShop(UUID owner, Inventory inv, int pag) { stocks.add(this); } - public static Optional getStockShopByOwner(UUID owner, int pag) { return stocks.parallelStream().filter(t -> t.owner.equals(owner) && t.pag == pag).findFirst(); } + public static void purgePlayerStock(UUID stockOwner) { + List stockDelete = new ArrayList<>(); + for(StockShop stock : stocks) { + if(stock.isOwner(stockOwner)) + stockDelete.add(stock); + } + for(StockShop stock : stockDelete) + stock.deleteStock(true, !expiredStockOwners.contains(stock.getOwner())); + } + + public static void expiredStock() { + if(maxStockDays <= 0) + return; + exemptStockListInactive = exemptStockExpiringList.size() == 1 && exemptStockExpiringList.get(0).equals("00000000-0000-0000-0000-000000000000"); + List stockDelete = new ArrayList<>(); + List deleteCount = new ArrayList<>(); + for(StockShop stock : stocks) { + if(stock.hasStockExpired()) { + stockDelete.add(stock); + stock.getInventory().clear(); + if(!deleteCount.contains(stock.getOwner())) + deleteCount.add(stock.getOwner()); + } + } + for(StockShop stock : stockDelete) + stock.deleteStock(true, !expiredStockOwners.contains(stock.getOwner())); + if(!deleteCount.isEmpty()) + Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] Stock has expired for " + deleteCount.size() + " players."); + } + + public boolean hasStockExpired() { + if(!exemptStockListInactive) + for(String exemptCheck : exemptStockExpiringList) { + if(exemptCheck != null && exemptCheck.equalsIgnoreCase(this.owner.toString())) + return false; + } + OfflinePlayer player = Bukkit.getOfflinePlayer(this.owner); + if(player.isOnline()) + return false; + return ((System.currentTimeMillis() - player.getLastPlayed()) / millisecondsPerDay) >= maxStockDays; + } + + public void deleteStock(boolean removalOfStock, boolean deleteFromDatabase) { + if(removalOfStock) { + stocks.remove(this); + if(deleteFromDatabase) { + expiredStockOwners.add(owner); + Bukkit.getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> { + PreparedStatement stmt1 = null; + try { + stmt1 = iShop.getConnection().prepareStatement("DELETE FROM zooMercaStocks WHERE owner = ?;"); + stmt1.setString(1, owner.toString()); + stmt1.execute(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (stmt1 != null) + stmt1.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + } + } public static void saveData() { if(!hasStock()) @@ -54,19 +126,9 @@ public static void saveData() { private void saveStockData() { PreparedStatement stmt = null; try { - stmt = iShop.getConnection().prepareStatement("INSERT INTO zooMercaStocks (owner, items, pag) VALUES (?,?,?);"); - JsonArray items = new JsonArray(); - for(ItemStack item : inventory.getContents()) { - if(item == null) - continue; - YamlConfiguration config = new YamlConfiguration(); - item.serialize().forEach(config::set); - String itemRaw = config.saveToString(); - items.add(itemRaw); - } - String itemsJson = (new Gson()).toJson(items); + stmt = iShop.getConnection().prepareStatement("INSERT INTO zooMercaStocks (owner, itemsNew, pag) VALUES (?,?,?);"); stmt.setString(1, owner.toString()); - stmt.setString(2, itemsJson); + stmt.setBytes(2,iShop.encodeByte(inventory.getContents())); stmt.setInt(3, pag); stmt.execute(); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/com/minedhype/ishop/TabComplete.java b/src/com/minedhype/ishop/TabComplete.java new file mode 100644 index 0000000..ba55a19 --- /dev/null +++ b/src/com/minedhype/ishop/TabComplete.java @@ -0,0 +1,49 @@ +package com.minedhype.ishop; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +public class TabComplete implements TabCompleter { + + private static final List items = Arrays.stream(Material.values()).filter(Material::isItem).map(material -> material.getKey().getKey()).toList(); + private static final List shopCommands = Arrays.asList("adminshop","copy","count","create","createshop","createlocation","delete","deleteid","deletelocation","find","findbook","list","listadmin","liststock","lockdown","manage","managestock","move","out","purgestock","reload","removeallshops","shops","sold","stock","view"); + public static final List enchantments = Arrays.asList("aqua_affinity","bane_of_arthropods","binding_curse","blast_protection","breach","channeling","density","depth_strider","efficiency","feather_falling","fire_aspect","fire_protection","flame","fortune","frost_walker","impaling","infinity","knockback","looting","loyalty","luck_of_the_sea","lunge","lure","mending","multishot","piercing","power","projectile_protection","protection","punch","quick_charge","respiration","riptide","sharpness","silk_touch","smite","soul_speed","sweeping_edge","swift_sneak","thorns","unbreaking","vanishing_curse","wind_burst"); + + @Override + public List onTabComplete(CommandSender sender, Command cmd, String label, String[] args) { + if(args[0].equalsIgnoreCase("find") || args[0].equalsIgnoreCase("count")) { + if(args.length == 2) + return StringUtil.copyPartialMatches(args[1].toLowerCase(), items, new ArrayList<>()); + return Collections.emptyList(); + } + else if(args[0].equalsIgnoreCase("findbook")) { + if(args.length == 2) + return StringUtil.copyPartialMatches(args[1].toUpperCase(), enchantments, new ArrayList<>()); + return Collections.emptyList(); + } + if(args[0].equalsIgnoreCase("createlocation") || args[0].equalsIgnoreCase("createshop") || args[0].equalsIgnoreCase("list") || args[0].equalsIgnoreCase("managestock") || args[0].equalsIgnoreCase("out") || args[0].equalsIgnoreCase("purgestock") || args[0].equalsIgnoreCase("removeallshops")) { + if(args.length == 2) { + final List playersOnline = new ArrayList<>(); + for(Player player:Bukkit.getOnlinePlayers()) { + playersOnline.add(player.getName()); + } + return StringUtil.copyPartialMatches(args[1].toLowerCase(), playersOnline, new ArrayList<>()); + } + return Collections.emptyList(); + } + else { + if(args.length < 2) + return StringUtil.copyPartialMatches(args[0].toLowerCase(), shopCommands, new ArrayList<>()); + return Collections.emptyList(); + } + } +} \ No newline at end of file diff --git a/src/com/minedhype/ishop/Utils.java b/src/com/minedhype/ishop/Utils.java index 1935905..221263d 100644 --- a/src/com/minedhype/ishop/Utils.java +++ b/src/com/minedhype/ishop/Utils.java @@ -4,18 +4,28 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import com.minedhype.ishop.inventories.InvAdminShop; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; public class Utils { public static boolean hasStock(Player player, ItemStack item) { if(item == null || item.getType().equals(Material.AIR)) return true; - return player.getInventory().containsAtLeast(item, item.getAmount()); + if(item.hasItemMeta()) { + ItemStack itemClone = item.clone(); + return (player.getInventory().containsAtLeast(item, item.getAmount()) || player.getInventory().containsAtLeast(itemClone, itemClone.getAmount())); + } else + return player.getInventory().containsAtLeast(item, item.getAmount()); } public static boolean hasStock(Shop shop, ItemStack item) { if(shop.isAdmin() || item == null || item.getType().equals(Material.AIR)) return true; - int max = iShop.config.getInt("stockPages"); + int max; + if(InvAdminShop.usePerms) + max = InvAdminShop.permissionMax; + else + max = InvAdminShop.maxPages; int amount = item.getAmount(); int itemAmountCount = 0; for(int i=0; i= amount) return true; } @@ -40,7 +64,21 @@ public static boolean hasDoubleItemStock(Player player, ItemStack item, ItemStac if((item == null && item2 == null) || (item.getType().equals(Material.AIR) && item2.getType().equals(Material.AIR))) return true; int bothAmounts = item.getAmount() + item2.getAmount(); - return player.getInventory().containsAtLeast(item, bothAmounts); + if(!item.hasItemMeta() && !item2.hasItemMeta()) + return player.getInventory().containsAtLeast(item, bothAmounts); + else { + boolean hasItems = false; + boolean hasItems2 = false; + if(item.hasItemMeta()) { + ItemStack itemClone = item.clone(); + hasItems = player.getInventory().containsAtLeast(itemClone, bothAmounts); + } + if(item2.hasItemMeta()) { + ItemStack item2Clone = item2.clone(); + hasItems2 = player.getInventory().containsAtLeast(item2Clone, bothAmounts); + } + return (player.getInventory().containsAtLeast(item, bothAmounts) || player.getInventory().containsAtLeast(item2, bothAmounts) || hasItems || hasItems2); + } } public static boolean hasDoubleItemStock(Shop shop, ItemStack item, ItemStack item2) { @@ -50,23 +88,39 @@ public static boolean hasDoubleItemStock(Shop shop, ItemStack item, ItemStack it int item2Amount = 0; int item1Total = item.getAmount(); int item2Total = item2.getAmount(); - int max = iShop.config.getInt("stockPages"); + int max; + if(InvAdminShop.usePerms) + max = InvAdminShop.permissionMax; + else + max = InvAdminShop.maxPages; for(int i=0; i stockStore = StockShop.getStockShopByOwner(shop.getOwner(), i); if(!stockStore.isPresent()) continue; if(stockStore.get().getInventory().contains(item.getType()) || stockStore.get().getInventory().contains(item2.getType())) { - for(int j=0; j item1Total && item.isSimilar(item2)) { - int difference = item1Amount - item1Total; - item2Amount += difference; - } + if(!item.hasItemMeta() && !item2.hasItemMeta()) { + if(item1Amount < item1Total && stockStore.get().getInventory().getItem(j).isSimilar(item)) { + item1Amount += stockStore.get().getInventory().getItem(j).getAmount(); + if(item1Amount > item1Total && item.isSimilar(item2)) { + int difference = item1Amount - item1Total; + item2Amount += difference; + } + } else if(item2Amount < item2Total && stockStore.get().getInventory().getItem(j).isSimilar(item2)) + item2Amount += stockStore.get().getInventory().getItem(j).getAmount(); + } else { + ItemStack itemClone = item.clone(); + ItemStack item2Clone = item2.clone(); + if(item1Amount < item1Total && (stockStore.get().getInventory().getItem(j).isSimilar(item) || stockStore.get().getInventory().getItem(j).isSimilar(itemClone))) { + item1Amount += stockStore.get().getInventory().getItem(j).getAmount(); + if(item1Amount > item1Total && (item.isSimilar(item2) || item.isSimilar(item2Clone) || itemClone.isSimilar(item2) || itemClone.isSimilar(item2Clone))) { + int difference = item1Amount - item1Total; + item2Amount += difference; + } + } else if(item2Amount < item2Total && (stockStore.get().getInventory().getItem(j).isSimilar(item2) || stockStore.get().getInventory().getItem(j).isSimilar(item2Clone))) + item2Amount += stockStore.get().getInventory().getItem(j).getAmount(); } - else if(item2Amount < item2Total && stockStore.get().getInventory().getItem(j).isSimilar(item2)) - item2Amount += stockStore.get().getInventory().getItem(j).getAmount(); } if(item1Amount >= item1Total && item2Amount >= item2Total) return true; @@ -75,4 +129,37 @@ else if(item2Amount < item2Total && stockStore.get().getInventory().getItem(j).i } return false; } + + public static boolean hasEnchantment(Shop shop, String bookName, boolean doubleBook) { + if(shop.isAdmin()) + return true; + int max; + if(InvAdminShop.usePerms) + max = InvAdminShop.permissionMax; + else + max = InvAdminShop.maxPages; + int count = 0; + for(int i=0; i stockStore = StockShop.getStockShopByOwner(shop.getOwner(), i); + if(!stockStore.isPresent()) + continue; + if(stockStore.get().getInventory().contains(Material.ENCHANTED_BOOK)) { + for(int j=0; j1) + return true; + } + else + return true; + } + } + } + } + } + return false; + } } diff --git a/src/com/minedhype/ishop/gui/GUI.java b/src/com/minedhype/ishop/gui/GUI.java index 9041d20..7adce99 100644 --- a/src/com/minedhype/ishop/gui/GUI.java +++ b/src/com/minedhype/ishop/gui/GUI.java @@ -35,17 +35,16 @@ public void onClose(InventoryCloseEvent e) { } public static ItemStack createItem(Material material, String number, short data) { ItemStack item = new ItemStack(material); ItemMeta meta = item.getItemMeta(); - if(meta != null) + if(meta != null) { meta.setDisplayName(ChatColor.RESET + number); - else - meta.setDisplayName("unnamed"); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); - meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); - meta.addItemFlags(ItemFlag.HIDE_DESTROYS); - meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); - meta.addItemFlags(ItemFlag.HIDE_PLACED_ON); - meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); - item.setItemMeta(meta); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + meta.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); + meta.addItemFlags(ItemFlag.HIDE_DESTROYS); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + meta.addItemFlags(ItemFlag.HIDE_PLACED_ON); + meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); + item.setItemMeta(meta); + } return item; } diff --git a/src/com/minedhype/ishop/iShop.java b/src/com/minedhype/ishop/iShop.java index fc49b47..f584e8b 100644 --- a/src/com/minedhype/ishop/iShop.java +++ b/src/com/minedhype/ishop/iShop.java @@ -1,7 +1,9 @@ package com.minedhype.ishop; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -12,31 +14,58 @@ import java.util.Optional; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; import com.minedhype.ishop.gui.GUIEvent; -import com.minedhype.ishop.MetricsLite; +import com.minedhype.ishop.Metrics; import net.milkbowl.vault.economy.Economy; import org.bukkit.scheduler.BukkitTask; +import me.ryanhamshire.GriefPrevention.GriefPrevention; +import me.angeschossen.lands.api.LandsIntegration; public class iShop extends JavaPlugin { File configFile; public static FileConfiguration config; public static WorldGuardLoader wgLoader = null; - private static BukkitTask expiredTask, saveTask, tickTask; + public static GriefPrevention gpLoader = null; + public static LandsIntegration lands = null; + public static boolean superiorSkyblock2Check; + public static boolean townyCheck; + private static BukkitTask expiredShopTask, expiredStockTask, saveTask, tickTask; private static Connection connection = null; private static Economy economy = null; private static String chainConnect; + private boolean lockdown; @Override public void onLoad() { + Plugin gpCheck = Bukkit.getPluginManager().getPlugin("GriefPrevention"); + if(gpCheck != null) + gpLoader = (GriefPrevention)gpCheck; Plugin wgCheck = Bukkit.getPluginManager().getPlugin("WorldGuard"); if(wgCheck != null) wgLoader = new WorldGuardLoader(); + Plugin landsCheck = Bukkit.getPluginManager().getPlugin("Lands"); + if(landsCheck != null) + lands = LandsIntegration.of(iShop.getPlugin()); + Plugin superiorSkyblock2Enabled = Bukkit.getPluginManager().getPlugin("SuperiorSkyblock2"); + if(superiorSkyblock2Enabled != null) + superiorSkyblock2Check = true; + else + superiorSkyblock2Check = false; + Plugin townyEnabled = Bukkit.getPluginManager().getPlugin("Towny"); + if(townyEnabled != null) + townyCheck = true; + else + townyCheck = false; } @Override @@ -55,7 +84,13 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new EventShop(), this); getServer().getPluginManager().registerEvents(new GUIEvent(), this); getCommand("ishop").setExecutor(new CommandShop()); + getCommand("ishop").setTabCompleter(new TabComplete()); + try { + connection = DriverManager.getConnection(chainConnect); + this.createTables(); + } catch(Exception e) { e.printStackTrace(); } int delayTime; + int saveDatabaseTime; try { delayTime = config.getInt("shopsDatabaseLoadDelay"); } catch(Exception e) { delayTime = 0; } @@ -63,18 +98,32 @@ public void onEnable() { delayTime = 1; else delayTime*=20; + try { + saveDatabaseTime = config.getInt("saveDatabase"); + } catch(Exception e) { saveDatabaseTime = 15; } + if(saveDatabaseTime < 5) + saveDatabaseTime = 5; + (saveDatabaseTime)*=60; + (saveDatabaseTime)*=20; Bukkit.getScheduler().runTaskLater(this, () -> { try { - connection = DriverManager.getConnection(chainConnect); - this.createTables(); Shop.loadData(); } catch(Exception e) { e.printStackTrace(); } }, delayTime); - expiredTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, Shop::expiredShops, delayTime+20, 20000); - saveTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, Shop::saveData, delayTime+1200, 6000); - tickTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, Shop::tickShops, delayTime+150, 50); - Bukkit.getScheduler().runTaskLaterAsynchronously(this, Shop::getPlayersShopList, delayTime+100); - MetricsLite metrics = new MetricsLite(this, 9189); + lockdown = config.getBoolean("lockdownEnabledOnStart"); + expiredShopTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, Shop::expiredShops, delayTime+9, 20000); + expiredStockTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, StockShop::expiredStock, delayTime+200, 35000); + saveTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> { + try { + Shop.saveData(false); + } catch (Exception e) { Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW + "[iShop] Warning: Tried saving to database while being modified at the same time. Saving will continue and try again later, but will also save to database safely upon server shutdown."); } + }, delayTime+1200, saveDatabaseTime); + if(Shop.shopEnabled && Shop.particleEffects) + tickTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, Shop::tickShops, delayTime+250, 50); + Bukkit.getScheduler().runTaskLaterAsynchronously(this, Shop::getPlayersShopList, delayTime+160); + Bukkit.getScheduler().runTaskLaterAsynchronously(this, StockShop::getPlayersStockList, delayTime+300); + Bukkit.getScheduler().runTaskLaterAsynchronously(this, Shop::removeEmptyShopTrade, delayTime+100); + Metrics metrics = new Metrics(this, 9189); new UpdateChecker(this, 84555).getVersion(version -> { if(!this.getDescription().getVersion().equalsIgnoreCase(version)) getServer().getConsoleSender().sendMessage(ChatColor.RED + "[iShop] There is a new update available! - https://www.spigotmc.org/resources/ishop.84555/"); @@ -83,17 +132,48 @@ public void onEnable() { @Override public void onDisable() { - tickTask.cancel(); - expiredTask.cancel(); + EventShop.shuttingDown = true; + for(Player player:Bukkit.getServer().getOnlinePlayers()) + player.closeInventory(); + if(Bukkit.getScheduler().isCurrentlyRunning(tickTask.getTaskId())) { + while(Bukkit.getScheduler().isCurrentlyRunning(tickTask.getTaskId())) + ; + tickTask.cancel(); + } + else + tickTask.cancel(); if(Bukkit.getScheduler().isCurrentlyRunning(saveTask.getTaskId())) { while(Bukkit.getScheduler().isCurrentlyRunning(saveTask.getTaskId())) ; saveTask.cancel(); } - else { + else saveTask.cancel(); - Shop.saveData(); + if(Bukkit.getScheduler().isCurrentlyRunning(expiredStockTask.getTaskId())) { + while(Bukkit.getScheduler().isCurrentlyRunning(expiredStockTask.getTaskId())) + ; + expiredStockTask.cancel(); } + else + expiredStockTask.cancel(); + if(Bukkit.getScheduler().isCurrentlyRunning(expiredShopTask.getTaskId())) { + while(Bukkit.getScheduler().isCurrentlyRunning(expiredShopTask.getTaskId())) + ; + expiredShopTask.cancel(); + } + else + expiredShopTask.cancel(); + if(!tickTask.isCancelled()) + tickTask.cancel(); + if(!saveTask.isCancelled()) + saveTask.cancel(); + if(!expiredStockTask.isCancelled()) + expiredStockTask.cancel(); + if(!expiredShopTask.isCancelled()) + expiredShopTask.cancel(); + getServer().getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] Safely saving shops & stock items to database, please wait & do not kill server process..."); + Shop.saveData(true); + getServer().getConsoleSender().sendMessage(ChatColor.GREEN + "[iShop] Saving complete!"); if(connection != null) { try { connection.close(); @@ -108,8 +188,8 @@ private void createTables() { try { stmts = new PreparedStatement[] { connection.prepareStatement("CREATE TABLE IF NOT EXISTS zooMercaTiendas(id INTEGER PRIMARY KEY autoincrement, location varchar(64), owner varchar(64));"), - connection.prepareStatement("CREATE TABLE IF NOT EXISTS zooMercaTiendasFilas(itemIn text, itemOut text, idTienda INTEGER);"), - connection.prepareStatement("CREATE TABLE IF NOT EXISTS zooMercaStocks(owner varchar(64), items JSON);") + connection.prepareStatement("CREATE TABLE IF NOT EXISTS zooMercaTiendasFilas(itemInNew blob, itemIn2New blob, itemOutNew blob, itemOut2New blob, idTienda INTEGER);"), + connection.prepareStatement("CREATE TABLE IF NOT EXISTS zooMercaStocks(owner varchar(64), itemsNew blob);") }; } catch(Exception e) { e.printStackTrace(); } for(PreparedStatement stmt : stmts) { @@ -120,20 +200,77 @@ private void createTables() { } List stmtsPatches = new ArrayList<>(); try { - stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN itemIn2 text NULL DEFAULT 'v: 2580\ntype: AIR\namount: 0' ")); - stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN itemOut2 text NULL DEFAULT 'v: 2580\ntype: AIR\namount: 0' ")); - stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN broadcast BOOLEAN DEFAULT 0")); - stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaStocks ADD COLUMN pag INTEGER DEFAULT 0")); + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN itemInNew blob;")); + } catch(Exception ignored) { } + try { + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN itemIn2New blob;")); + } catch(Exception ignored) { } + try { + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN itemOutNew blob;")); + } catch(Exception ignored) { } + try { + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN itemOut2New blob;")); + } catch(Exception ignored) { } + try { + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas ADD COLUMN broadcast BOOLEAN DEFAULT 0;")); + } catch(Exception ignored) { } + try { + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaStocks ADD COLUMN pag INTEGER DEFAULT 0;")); + } catch(Exception ignored) { } + try { + stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaStocks ADD COLUMN itemsNew blob;")); + } catch(Exception ignored) { } + try { stmtsPatches.add(connection.prepareStatement("ALTER TABLE zooMercaTiendas ADD COLUMN admin BOOLEAN DEFAULT FALSE;")); } catch(Exception ignored) { } for(PreparedStatement stmtsPatch : stmtsPatches) { try { stmtsPatch.execute(); stmtsPatch.close(); - } catch (Exception ignored) { } + } catch(Exception ignored) { } + } + } + + public static void dropOldColumns() { + PreparedStatement[] stmts = new PreparedStatement[] { }; + try { + stmts = new PreparedStatement[] { + connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas DROP COLUMN itemIn;"), + connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas DROP COLUMN itemIn2;"), + connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas DROP COLUMN itemOut;"), + connection.prepareStatement("ALTER TABLE zooMercaTiendasFilas DROP COLUMN itemOut2;"), + connection.prepareStatement("ALTER TABLE zooMercaStocks DROP COLUMN items;") + }; + } catch(Exception e) { e.printStackTrace(); } + for(PreparedStatement stmt : stmts) { + try { + stmt.execute(); + stmt.close(); + } catch(Exception e) { e.printStackTrace(); } } } + public static ItemStack[] decodeByte(InputStream inputStream) throws IOException, ClassNotFoundException { + if(inputStream == null || inputStream.available() == 0) + return new ItemStack[0]; + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + ItemStack[] items = new ItemStack[dataInput.readInt()]; + for(int i=0; i exemptExpiredShops = Arrays.asList("00000000-0000-0000-0000-000000000000"); config.set("exemptExpiringShops", exemptExpiredShops); config.set("outsideRegion", "&cCannot create shop outside of ishop region!"); - config.set("configVersion", "3.1"); - config.save(configFile); case "3.1": + config.set("strictStock", false); + case "3.2": + config.set("noClaimPermission", "&cYou do not have permission in this claim to create a shop!"); + config.set("pageSkipAhead", "5 Pages >>"); + config.set("pageSkipPrev", "<< 5 Pages"); + case "3.3": + config.set("maxStockPages", 10); + config.set("removedAllPlayer", "&6Removed&c %shops &6shop(s) for player:&c %p"); + case "3.4": + config.set("shopCreatedLoc", "&7Shop has been &aCREATED&7 for&a %p &7at &a%x &7/&a %y &7/&a %z &7in &a%w&7!"); + config.set("shopDeletedLoc", "&7Shop has been &cDELETED&7 for&a %p &7at &a%x &7/&a %y &7/&a %z &7in &a%w&7!"); + config.set("shopLocationErrorNum", "&cLocation coordinates must be a positive or negative number!"); + config.set("shopLocationErrorWorld", "&cLocation coordinates must be in a valid world!"); + case "3.5": + config.set("preventDuplicates", true); + config.set("preventAllDuplicates", false); + config.set("saveEmptyShops", true); + case "3.6": + config.set("saveDatabase", 15); + case "3.7": + config.set("skipPermsCheckForAdminCreateShop", true); + config.set("noPermissionToCreateShop", "&cYou do not have permission to create a shop at this location!"); + case "3.8": + config.set("publicFindCommand", true); + config.set("findCommandCooldown", 5); + config.set("publicMoveCommand", true); + config.set("cooldownMessageFind","&6You must wait &c%time second(s) &6before using find command again!"); + config.set("findError", "&cGiven item to search shops for does not exist!"); + config.set("listFind", "&6Listing all found shop(s) trading &a%item&6:"); + config.set("nolistFind", "&cNo shop(s) have been found!"); + config.set("shopMoved", "&6Shop &a#%id &6has been moved to new targeted location!"); + case "3.9": + config.set("location", "&6Shop&a %id &6location XYZ:&a %x &6/&a %y &6/&a %z &6in&a %world"); + case "3.10": + config.set("shopParticles", "happy_villager"); + case "3.11": + config.set("shopParticles", "happy_villager"); + config.set("maxStockInactiveDays", 0); + List exemptExpiredStock = Arrays.asList("00000000-0000-0000-0000-000000000000"); + config.set("exemptExpiringStock", exemptExpiredStock); + config.set("deleteStockWhenShopsExpire", false); + config.set("lockdownEnabledOnStart", false); + config.set("showStockListItemForAdmin", true); + config.set("purgePlayersStock", "&7Shop stock has been &cPURGED&7 for &c%p&7!"); + config.set("lockdown","&cShops and stock are currently on lockdown."); + config.set("enableLockdown", "&7Lockdown has been &aenabled&7. Only shop admins can access shops, stocks, and commands."); + config.set("disableLockdown", "&7Lockdown has been &cdisabled&7. Players can access shops, stocks, and commands."); + config.set("configVersion", "3.12"); + config.save(configFile); + case "3.12": break; } } catch(IOException | InvalidConfigurationException e) { e.printStackTrace(); } @@ -267,4 +452,6 @@ public void createConfig() { public static iShop getPlugin() { return iShop.getPlugin(iShop.class); } + public boolean checkLockdown() { return lockdown; } + public void setLockdown(boolean setLock) { lockdown = setLock; } } diff --git a/src/com/minedhype/ishop/inventories/InvAdminShop.java b/src/com/minedhype/ishop/inventories/InvAdminShop.java index 3caa95a..615a98c 100644 --- a/src/com/minedhype/ishop/inventories/InvAdminShop.java +++ b/src/com/minedhype/ishop/inventories/InvAdminShop.java @@ -3,6 +3,7 @@ import java.util.Optional; import com.minedhype.ishop.iShop; import com.minedhype.ishop.Messages; +import com.minedhype.ishop.Permission; import com.minedhype.ishop.RowStore; import com.minedhype.ishop.Shop; import com.minedhype.ishop.Utils; @@ -17,6 +18,9 @@ public class InvAdminShop extends GUI { public static boolean remoteManage = iShop.config.getBoolean("remoteManage"); public static boolean stockGUIShop = iShop.config.getBoolean("enableStockAccessFromShopGUI"); public static boolean stockCommandEnabled = iShop.config.getBoolean("enableStockCommand"); + public static boolean usePerms = iShop.config.getBoolean("usePermissions"); + public static int maxPages = iShop.config.getInt("stockPages"); + public static int permissionMax = iShop.config.getInt("maxStockPages"); private final Shop shop; public InvAdminShop(Shop shop, Player player) { @@ -76,7 +80,7 @@ private void updateItems(Player player) { final int index = y-1; if(row.isPresent()) { placeItem(y*9+x, GUI.createItem(Material.TNT, ChatColor.BOLD+ Messages.SHOP_TITLE_DELETE.toString()), p -> { - shop.delete(p, index); + shop.delete(index); InvAdminShop inv = new InvAdminShop(shop, p.getPlayer()); inv.open(p); }); @@ -91,6 +95,21 @@ private void updateItems(Player player) { placeItem(y * 9 + x, GUI.createItem(Material.CHEST, Messages.SHOP_TITLE_STOCK.toString()), p -> { p.closeInventory(); InvStock inv = InvStock.getInvStock(player.getUniqueId()); + int maxStockPages = maxPages; + if(usePerms) { + String permPrefix = Permission.SHOP_STOCK_PREFIX.toString(); + int maxPermPages = InvAdminShop.permissionMax; + boolean permissionFound = false; + for(int i=maxPermPages; i>0; i--) + if(player.hasPermission(permPrefix + i)) { + maxStockPages = i; + permissionFound = true; + break; + } + if(!permissionFound) + maxStockPages = maxPermPages; + } + inv.setMaxPages(maxStockPages); inv.setPag(0); InvStock.inShopInv.put(player, player.getUniqueId()); inv.open(player); diff --git a/src/com/minedhype/ishop/inventories/InvCreateRow.java b/src/com/minedhype/ishop/inventories/InvCreateRow.java index 4b6c647..e6db1b6 100644 --- a/src/com/minedhype/ishop/inventories/InvCreateRow.java +++ b/src/com/minedhype/ishop/inventories/InvCreateRow.java @@ -9,8 +9,9 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import com.minedhype.ishop.RowStore; import com.minedhype.ishop.Messages; +import com.minedhype.ishop.Permission; +import com.minedhype.ishop.RowStore; import com.minedhype.ishop.Shop; import com.minedhype.ishop.iShop; import com.minedhype.ishop.gui.GUI; @@ -25,6 +26,9 @@ public class InvCreateRow extends GUI { private ItemStack itemOut2; private final ItemStack airItem = new ItemStack(Material.AIR, 0); public static Boolean itemsDisabled = iShop.config.getBoolean("disabledItems"); + public static Boolean preventDupeTrades = iShop.config.getBoolean("preventDuplicates"); + public static Boolean preventAllDupeTrades = iShop.config.getBoolean("preventAllDuplicates"); + public static Boolean strictStock = iShop.config.getBoolean("strictStock"); public static List disabledItemList = iShop.config.getStringList("disabledItemsList"); public InvCreateRow(Shop shop, int index) { @@ -53,14 +57,24 @@ else if(i == 13) { return; if((itemOut == airItem && itemOut2 == airItem) || (itemIn == airItem && itemIn2 == airItem)) return; + ItemStack in1 = itemIn.clone(); + ItemStack in2 = itemIn2.clone(); + ItemStack out1 = itemOut.clone(); + ItemStack out2 = itemOut2.clone(); + if(!p.hasPermission(Permission.SHOP_ADMIN.toString())) { + if(preventAllDupeTrades) { + if(Shop.hasAnyDuplicateTrades(out1, out2, in1, in2, p.getUniqueId())) + return; + } + else if(preventDupeTrades) { + if(Shop.hasDuplicateTrades(out1, out2, in1, in2, shop.shopId())) + return; + } + } if(itemsDisabled) { for(String itemsList:disabledItemList) { Material disabledItemsList = Material.matchMaterial(itemsList); if(disabledItemsList != null) { - ItemStack in1 = itemIn.clone(); - ItemStack in2 = itemIn2.clone(); - ItemStack out1 = itemOut.clone(); - ItemStack out2 = itemOut2.clone(); if(in1.getType().equals(disabledItemsList) || in2.getType().equals(disabledItemsList) || out1.getType().equals(disabledItemsList) || out2.getType().equals(disabledItemsList)) return; if(in1.getType().toString().contains("SHULKER_BOX") && in1.getItemMeta() instanceof BlockStateMeta) { @@ -130,7 +144,7 @@ else if(out2.getType().equals(Material.BUNDLE)) { } } } - shop.getRows()[index] = new RowStore(itemOut, itemOut2, itemIn, itemIn2, false); + shop.getRows()[index] = new RowStore(out1, out2, in1, in2, false); InvAdminShop inv = new InvAdminShop(shop, p.getPlayer()); inv.open(p); }); @@ -158,7 +172,7 @@ public void onClick(InventoryClickEvent event) { if(inv.getType().equals(InventoryType.CHEST) && event.getView().getTitle().contains(Messages.SHOP_TITLE_CREATESHOP.toString())) { event.setCancelled(true); if(event.getRawSlot() == 10 || event.getRawSlot() == 11 || event.getRawSlot() == 15 || event.getRawSlot() == 16) { - ItemStack item = event.getCursor().clone(); + ItemStack item = event.getCursor().clone(); if(event.getClick().isRightClick()) item.setAmount(1); placeItem(event.getRawSlot(), item); diff --git a/src/com/minedhype/ishop/inventories/InvShopList.java b/src/com/minedhype/ishop/inventories/InvShopList.java index de5d53c..fa55439 100644 --- a/src/com/minedhype/ishop/inventories/InvShopList.java +++ b/src/com/minedhype/ishop/inventories/InvShopList.java @@ -18,10 +18,10 @@ import java.util.UUID; public class InvShopList extends GUI { - private final static ArrayList shopslist = new ArrayList<>(); + private int pag; private final ItemStack airItem = new ItemStack(Material.AIR, 0); + private static final ArrayList shopslist = new ArrayList<>(); private static final ItemStack playerHead = new ItemStack(Material.PLAYER_HEAD,1); - private int pag; private InvShopList(String shopTitle) { super(54, shopTitle); @@ -77,9 +77,14 @@ private static void getShopList(UUID uuid) { ItemStack item = new ItemStack(playerHead); SkullMeta skullMeta = (SkullMeta) item.getItemMeta(); OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(Shop.shopList.get(id)); - skullMeta.setOwningPlayer(offlinePlayer); + boolean playerNotFound = false; + try { + skullMeta.setOwningPlayer(offlinePlayer); } + catch(Exception e) { playerNotFound = true; } if(Shop.getShopById(id).get().isAdmin()) skullMeta.setDisplayName(Messages.ADMIN_SHOP_NUMBER.toString().replaceAll("%id", id.toString())); + else if(playerNotFound || offlinePlayer.getName() == null) + skullMeta.setDisplayName(Messages.SHOP_NUMBER.toString().replaceAll("%player", "Unknown Player").replaceAll("%id", id.toString())); else skullMeta.setDisplayName(Messages.SHOP_NUMBER.toString().replaceAll("%player", offlinePlayer.getName()).replaceAll("%id", id.toString())); List skullLore = new ArrayList<>(); @@ -105,7 +110,7 @@ public void open(Player player) { shopslist.clear(); final UUID playerUUID = player.getUniqueId(); final Player openPlayer = player; - Bukkit.getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> { + Bukkit.getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> { getShopList(playerUUID); Bukkit.getScheduler().runTask(iShop.getPlugin(), () -> { PlayerShopList(); diff --git a/src/com/minedhype/ishop/inventories/InvStock.java b/src/com/minedhype/ishop/inventories/InvStock.java index f386104..50140e5 100644 --- a/src/com/minedhype/ishop/inventories/InvStock.java +++ b/src/com/minedhype/ishop/inventories/InvStock.java @@ -5,6 +5,12 @@ import java.util.Optional; import java.util.UUID; import java.util.HashMap; +import com.minedhype.ishop.Messages; +import com.minedhype.ishop.Permission; +import com.minedhype.ishop.Shop; +import com.minedhype.ishop.StockShop; +import com.minedhype.ishop.gui.GUI; +import com.minedhype.ishop.iShop; import org.bukkit.Material; import org.bukkit.block.ShulkerBox; import org.bukkit.entity.Player; @@ -12,20 +18,19 @@ import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import com.minedhype.ishop.Messages; -import com.minedhype.ishop.StockShop; -import com.minedhype.ishop.iShop; -import com.minedhype.ishop.gui.GUI; import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BundleMeta; public class InvStock extends GUI { + public static boolean showStockListAdmin = iShop.config.getBoolean("showStockListItemForAdmin"); public static final HashMap inShopInv = new HashMap<>(); private static final List inventories = new ArrayList<>(); private final ItemStack airItem = new ItemStack(Material.AIR, 0); private final UUID owner; private int pag; + private int stockPages; + private Player player; private InvStock(UUID owner) { super(54, Messages.SHOP_TITLE_STOCK.toString()); @@ -41,51 +46,59 @@ public void onClick(InventoryClickEvent event) { super.onClick(event); if(event.getRawSlot() >= 45 && event.getRawSlot() < 54) return; - if(InvCreateRow.itemsDisabled && event.getRawSlot() >= 54) { + if(event.getRawSlot() >= 54 && !player.hasPermission(Permission.SHOP_ADMIN.toString())) { ItemStack item = event.getCurrentItem(); ItemStack item2 = event.getCursor(); - for(String itemsList:InvCreateRow.disabledItemList) { - Material disabledItemsList = Material.matchMaterial(itemsList); - if(item == null) - item = airItem; - if(item2 == null) - item2 = airItem; - if(disabledItemsList != null) { - if(!item.isSimilar(airItem)) { - if(item.getType().equals(disabledItemsList)) - return; - if(item.getType().toString().contains("SHULKER_BOX") && item.getItemMeta() instanceof BlockStateMeta) { - BlockStateMeta itemMeta1 = (BlockStateMeta) item.getItemMeta(); - ShulkerBox shulkerBox1 = (ShulkerBox) itemMeta1.getBlockState(); - if(shulkerBox1.getInventory().contains(disabledItemsList)) + if(item == null && item2 == null) + return; + if(item == null) + item = airItem; + if(item2 == null) + item2 = airItem; + if(InvCreateRow.strictStock) { + if(Shop.strictStockShopCheck(item,item2,owner)) + return; + } + if(InvCreateRow.itemsDisabled) { + for(String itemsList:InvCreateRow.disabledItemList) { + Material disabledItemsList = Material.matchMaterial(itemsList); + if(disabledItemsList != null) { + if(!item.isSimilar(airItem)) { + if(item.getType().equals(disabledItemsList)) return; - } else if(item.getType().equals(Material.BUNDLE)) { - BundleMeta bundleIn1 = (BundleMeta) item.getItemMeta(); - if(bundleIn1.hasItems()) { - ItemStack itemDisabledOut = new ItemStack(disabledItemsList); - List bundleIn1Items = bundleIn1.getItems(); - for(ItemStack bundleList : bundleIn1Items) - if(bundleList.isSimilar(itemDisabledOut)) - return; + if(item.getType().toString().contains("SHULKER_BOX") && item.getItemMeta() instanceof BlockStateMeta) { + BlockStateMeta itemMeta1 = (BlockStateMeta) item.getItemMeta(); + ShulkerBox shulkerBox1 = (ShulkerBox) itemMeta1.getBlockState(); + if(shulkerBox1.getInventory().contains(disabledItemsList)) + return; + } else if(item.getType().equals(Material.BUNDLE)) { + BundleMeta bundleIn1 = (BundleMeta) item.getItemMeta(); + if(bundleIn1.hasItems()) { + ItemStack itemDisabledOut = new ItemStack(disabledItemsList); + List bundleIn1Items = bundleIn1.getItems(); + for(ItemStack bundleList : bundleIn1Items) + if(bundleList.isSimilar(itemDisabledOut)) + return; + } } } - } - if(!item2.isSimilar(airItem)) { - if(item2.getType().equals(disabledItemsList)) - return; - if(item2.getType().toString().contains("SHULKER_BOX") && item2.getItemMeta() instanceof BlockStateMeta) { - BlockStateMeta itemMeta2 = (BlockStateMeta) item2.getItemMeta(); - ShulkerBox shulkerBox2 = (ShulkerBox) itemMeta2.getBlockState(); - if(shulkerBox2.getInventory().contains(disabledItemsList)) + if(!item2.isSimilar(airItem)) { + if(item2.getType().equals(disabledItemsList)) return; - } else if(item2.getType().equals(Material.BUNDLE)) { - BundleMeta bundleIn2 = (BundleMeta) item2.getItemMeta(); - if(bundleIn2.hasItems()) { - ItemStack itemDisabledOut = new ItemStack(disabledItemsList); - List bundleIn2Items = bundleIn2.getItems(); - for(ItemStack bundleList : bundleIn2Items) - if(bundleList.isSimilar(itemDisabledOut)) - return; + if(item2.getType().toString().contains("SHULKER_BOX") && item2.getItemMeta() instanceof BlockStateMeta) { + BlockStateMeta itemMeta2 = (BlockStateMeta) item2.getItemMeta(); + ShulkerBox shulkerBox2 = (ShulkerBox) itemMeta2.getBlockState(); + if(shulkerBox2.getInventory().contains(disabledItemsList)) + return; + } else if(item2.getType().equals(Material.BUNDLE)) { + BundleMeta bundleIn2 = (BundleMeta) item2.getItemMeta(); + if(bundleIn2.hasItems()) { + ItemStack itemDisabledOut = new ItemStack(disabledItemsList); + List bundleIn2Items = bundleIn2.getItems(); + for(ItemStack bundleList : bundleIn2Items) + if(bundleList.isSimilar(itemDisabledOut)) + return; + } } } } @@ -102,12 +115,20 @@ public void refreshItems() { Inventory inv = stock.getInventory(); for(int i=0; i<45; i++) placeItem(i, inv.getItem(i)); - for(int i=45; i<54; i++) { - if(i == 47 && pag > 0) + if(i == 46 && pag > 4 && stockPages >= 10) + placeItem(i, GUI.createItem(Material.SPECTRAL_ARROW, Messages.SHOP_PAGE_SKIPPREV.toString()), p -> openPage(p, pag-5)); + else if(i == 47 && pag > 0) placeItem(i, GUI.createItem(Material.ARROW, Messages.SHOP_PAGE + " " + (pag)), p -> openPage(p, pag-1)); - else if(i == 51 && pag < iShop.config.getInt("stockPages")-1) + else if(i == 49 && showStockListAdmin && player != null && player.hasPermission(Permission.SHOP_ADMIN.toString())) + placeItem(i, GUI.createItem(Material.END_CRYSTAL,"List of Players with Stock"), p -> { + p.closeInventory(); + p.performCommand("shop liststock"); + }); + else if(i == 51 && pag < stockPages-1) placeItem(i, GUI.createItem(Material.ARROW, Messages.SHOP_PAGE + " " + (pag+2)), p -> openPage(p, pag+1)); + else if(i == 52 && pag < stockPages-5 && stockPages >= 10) + placeItem(i, GUI.createItem(Material.SPECTRAL_ARROW, Messages.SHOP_PAGE_SKIPAHEAD.toString()), p -> openPage(p, pag+5)); else placeItem(i, GUI.createItem(Material.BLACK_STAINED_GLASS_PANE, "")); } @@ -123,6 +144,7 @@ private void openPage(Player player, int pag) { @Override public void open(Player player) { + this.player = player; refreshItems(); super.open(player); } @@ -139,4 +161,7 @@ public void onClose(InventoryCloseEvent event) { public void setPag(int pag) { this.pag = pag; } + public void setMaxPages(int maxPages) { + this.stockPages = maxPages; + } } diff --git a/src/com/minedhype/ishop/inventories/InvStockList.java b/src/com/minedhype/ishop/inventories/InvStockList.java new file mode 100644 index 0000000..d006573 --- /dev/null +++ b/src/com/minedhype/ishop/inventories/InvStockList.java @@ -0,0 +1,117 @@ +package com.minedhype.ishop.inventories; + +import com.minedhype.ishop.iShop; +import com.minedhype.ishop.StockShop; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import com.minedhype.ishop.Messages; +import com.minedhype.ishop.gui.GUI; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +public class InvStockList extends GUI { + + private int pag; + private final ItemStack airItem = new ItemStack(Material.AIR, 0); + private static final ArrayList stocklist = new ArrayList<>(); + private static final ItemStack playerHead = new ItemStack(Material.PLAYER_HEAD,1); + private InvStockList(String stockTitle) { super(54, stockTitle); } + public static InvStockList setStockTitle(String stockTitle) { return new InvStockList(stockTitle); } + public void setPag(int pag) { this.pag = pag; } + + @Override + public void onClick(InventoryClickEvent event) { + super.onClick(event); + if(event.getRawSlot() >= 45 && event.getRawSlot() < 54) + return; + if(!event.getAction().equals(InventoryAction.PLACE_ALL) && !event.getAction().equals(InventoryAction.PICKUP_ALL)) + return; + if(event.getCurrentItem() == null || event.getCurrentItem().getType().isAir()) + return; + if(event.isLeftClick() && Objects.requireNonNull(event.getCurrentItem().getItemMeta()).hasLore() && event.getCurrentItem().getType().equals(Material.PLAYER_HEAD)) { + event.setCancelled(true); + Player player = (Player) event.getWhoClicked(); + player.closeInventory(); + player.performCommand("shop managestock " + event.getCurrentItem().getItemMeta().getDisplayName()); + } + } + + private void PlayerStockList() { + int index = pag * 45; + for(int i = 0; i < 45; i++) { + if(index <= stocklist.size()-1) { + placeItem(i, stocklist.get(index)); + index++; + } else + placeItem(i, airItem); + } + int stockListPages = (int)Math.ceil(stocklist.size()-1)/44; + for(int i=45; i<54; i++) { + if(i == 47 && pag > 0) + placeItem(i, GUI.createItem(Material.ARROW, Messages.SHOP_PAGE + " " + (pag)), p -> openPage(p, pag-1)); + else if(i == 51 && pag < stockListPages) + placeItem(i, GUI.createItem(Material.ARROW, Messages.SHOP_PAGE + " " + (pag+2)), p -> openPage(p, pag+1)); + else + placeItem(i, GUI.createItem(Material.BLACK_STAINED_GLASS_PANE, "")); + } + } + + private static void getStockList() { + for(UUID uuid : StockShop.stockList.keySet()) { + if(uuid != null) { + ItemStack item = new ItemStack(playerHead); + SkullMeta skullMeta = (SkullMeta) item.getItemMeta(); + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); + boolean playerNotFound = false; + try { + skullMeta.setOwningPlayer(offlinePlayer); + } catch (Exception e) { + playerNotFound = true; + } + if(offlinePlayer.getName() != null && !playerNotFound) + skullMeta.setDisplayName(offlinePlayer.getName()); + List skullLore = new ArrayList<>(); + skullLore.add(uuid.toString()); + skullMeta.setLore(skullLore); + item.setItemMeta(skullMeta); + stocklist.add(item); + } + } + } + + private void openPage(Player player, int pag) { + for(int i=45; i<54; i++) + placeItem(i, new ItemStack(airItem)); + player.closeInventory(); + this.pag = pag; + this.open(player); + } + + @Override + public void open(Player player) { + stocklist.clear(); + final Player openPlayer = player; + Bukkit.getScheduler().runTaskAsynchronously(iShop.getPlugin(), () -> { + getStockList(); + Bukkit.getScheduler().runTask(iShop.getPlugin(), () -> { + PlayerStockList(); + super.open(openPlayer); + }); + }); + } + + @Override + public void onDrag(InventoryDragEvent event) { + super.onDrag(event); + event.setCancelled(true); + } +} \ No newline at end of file diff --git a/src/resources/config.yml b/src/resources/config.yml index f2366d2..57077e7 100644 --- a/src/resources/config.yml +++ b/src/resources/config.yml @@ -53,9 +53,12 @@ enableStockAccessFromShopGUI: true # Players with ishop.stock permission will bypass if disabled enableStockCommand: true -# Number of pages to have in the stock GUI +# Number of default stock pages to have in the stock GUI stockPages: 5 +# Maximum number of stock pages to have in the stock GUI if usePermissions is set to true +maxStockPages: 10 + # Enable sending of messages when players have purchased or sold item(s) from shops enableShopNotifications: true @@ -102,7 +105,15 @@ maxInactiveDays: 0 exemptExpiringShops: - 00000000-0000-0000-0000-000000000000 -# Delete block that the shop is located at when deleting a shop +# Amount of days a player is inactive before deleting their stock inventory +# Set to 0 to disable +maxStockInactiveDays: 0 + +# List of player UUID's that will be exempt from stock expiring if maxStockInactiveDays is set above 0 +exemptExpiringStock: + - 00000000-0000-0000-0000-000000000000 + +# Remove block that the shop is located at when deleting or moving a shop deleteBlock: false # Show particle effects on active shop blocks @@ -157,6 +168,45 @@ stockRangeLimitFromShop: 0 # Set to true if you would like '/shop stock' command range limit to be enforced using stockRangeLimitFromShop distance stockRangeLimitUsingCommand: false +# This will only allow items that are in active shop trades to be added and stored in stock inventory +strictStock: false + +# Prevent players from creating duplicate item trades within each shop they own +preventDuplicates: true + +# Prevent players from creating duplicate item trades from any shop they own +preventAllDuplicates: false + +# Save shops with no trades to database on server shutdown +saveEmptyShops: true + +# Save shops and stock to database interval in minutes. Database will auto save upon server shutdown. Default is every 15 minutes +saveDatabase: 15 + +# When a player with iShop.admin permission uses command createshop and is not a server operator, skip checking for permission within GriefPrevention, Lands, SuperiorSkyblock2, Towny, and WorldGuard +skipPermsCheckForAdminCreateShop: true + +# Allow everyone to use "/shop find " command to search all player shops that are selling given item +publicFindCommand: true + +# Time in seconds players must wait to use find command again. Set to 0 to disable +findCommandCooldown: 5 + +# Allow everyone to use '/shop move' command to move their shops to a new location +publicMoveCommand: true + +# Change the particle effect (requires server restart) for shops to any Enum Constant Particle found at https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Particle.html +shopParticles: "happy_villager" + +# Delete player stock inventory when their shops expire +deleteStockWhenShopsExpire: false + +# Setting to true will enable lockdown mode on start up. Only players with the ishop.admin permission can access shops, stocks, and commands during lockdown mode. Can toggle on/off using lockdown command. +lockdownEnabledOnStart: false + +# Enable players with ishop.admin permission to see clickable End Crystal item for player stock list while in stock inventory +showStockListItemForAdmin: true + # Customizable Messages adminShop: "Admin Shop #%id" adminShopDisabled: "&cAdmin shops have been disabled!" @@ -172,7 +222,7 @@ deleteTitle: "DELETE" disabledShopBlock: "&cCannot create shop location when shop blocks are disabled!" existingShop: "&cCannot create shop where existing shop is already located!" foundShops: "&6Found&a %shops &6shop(s) for player:&a %p" -location: "&6Shop&a %id &6location XYZ: " +location: "&6Shop&a %id &6location XYZ:&a %x &6/&a %y &6/&a %z &6in&a %world" noItems: "&cYou do not have enough item(s) to buy from this shop!" noMoney: "&cYou do not have enough money to create a shop! You need at least &o$" noPermissions: "&cYou do not have permission for this command!" @@ -233,6 +283,23 @@ countAmount: "&aTotal amount of %item found: %amount" countEmpty: "&cCannot find any %item in stock inventory!" countError: "&cGiven item to search stock for does not exist!" outsideRegion: "&cCannot create shop outside of ishop region!" - +noClaimPermission: "&cYou do not have permission in this claim to create a shop!" +pageSkipAhead: "5 Pages >>" +pageSkipPrev: "<< 5 Pages" +removedAllPlayer: "&6Removed&c %shops &6shop(s) for player:&c %p" +shopCreatedLoc: "&7Shop has been &aCREATED&7 for&a %p &7at &a%x &7/&a %y &7/&a %z &7in &a%w&7!" +shopDeletedLoc: "&7Shop has been &cDELETED&7 for&a %p &7at &a%x &7/&a %y &7/&a %z &7in &a%w&7!" +shopLocationErrorNum: "&cLocation coordinates must be a positive or negative number!" +shopLocationErrorWorld: "&cLocation coordinates must be in a valid world!" +noPermissionToCreateShop: "&cYou do not have permission to create a shop at this location!" +cooldownMessageFind: "&6You must wait &c%time second(s) &6before using find command again!" +findError: "&cGiven item to search shops for does not exist!" +listFind: "&6Listing all found shop(s) trading &a%item&6:" +nolistFind: "&cNo shop(s) have been found!" +shopMoved: "&6Shop &a#%id &6has been moved to new targeted location!" +purgePlayersStock: "&7Shop stock has been &cPURGED&7 for &c%p&7!" +lockdown: "&cShops and stock are currently on lockdown." +enableLockdown: "&7Lockdown has been &aenabled&7. Only shop admins can access shops, stocks, and commands." +disableLockdown: "&7Lockdown has been &cdisabled&7. Players can access shops, stocks, and commands." # Do not edit this! -configVersion: 3.1 \ No newline at end of file +configVersion: 3.12 \ No newline at end of file diff --git a/src/resources/plugin.yml b/src/resources/plugin.yml index a503688..7d569cb 100644 --- a/src/resources/plugin.yml +++ b/src/resources/plugin.yml @@ -4,8 +4,8 @@ description: Item trading shops with unique GUI! author: Beez0r website: https://minedhype.com version: "${project.version}" -api-version: "1.17" -softdepend: [Vault, WorldGuard] +api-version: "26.1" +softdepend: [GriefPrevention, Lands, SuperiorSkyblock2, Towny, Vault, WorldGuard] commands: ishop: description: "/shop"