shopLocationValues = Collections.unmodifiableCollection(shopLocation.values());
+ private final ShopChest plugin;
+
+ public ShopUtils(ShopChest plugin) {
+ this.plugin = plugin;
+ }
+
+ /**
+ * Get the shop at a given location
+ *
+ * @param location Location of the shop
+ * @return Shop at the given location or null if no shop is found there
+ */
+ public Shop getShop(Location location) {
+ Location newLocation = new Location(location.getWorld(), location.getBlockX(),
+ location.getBlockY(), location.getBlockZ());
+
+ return shopLocation.get(newLocation);
+ }
+
+ /**
+ * Checks whether there is a shop at a given location
+ * @param location Location to check
+ * @return Whether there is a shop at the given location
+ */
+ public boolean isShop(Location location) {
+ return getShop(location) != null;
+ }
+
+ /**
+ * Get a collection of all loaded shops
+ *
+ * This collection is safe to use for looping over and removing shops.
+ *
+ * @return Read-only collection of all shops, may contain duplicates for double chests
+ */
+ public Collection getShops() {
+ return Collections.unmodifiableCollection(new ArrayList<>(shopLocationValues));
+ }
+
+ /**
+ * Get all shops
+ *
+ * @see #getShops()
+ * @return Copy of collection of all shops, may contain duplicates
+ * @deprecated Use {@link #getShops()} instead
+ */
+ @Deprecated
+ public Collection getShopsCopy() {
+ return new ArrayList<>(getShops());
+ }
+
+ /**
+ * Add a shop
+ * @param shop Shop to add
+ * @param addToDatabase Whether the shop should also be added to the database
+ * @param callback Callback that - if succeeded - returns the ID the shop had or was given (as {@code int})
+ */
+ public void addShop(Shop shop, boolean addToDatabase, Callback callback) {
+ InventoryHolder ih = shop.getInventoryHolder();
+ plugin.debug("Adding shop... (#" + shop.getID() + ")");
+
+ if (ih instanceof DoubleChest) {
+ DoubleChest dc = (DoubleChest) ih;
+ Chest r = (Chest) dc.getRightSide();
+ Chest l = (Chest) dc.getLeftSide();
+
+ plugin.debug("Added shop as double chest. (#" + shop.getID() + ")");
+
+ shopLocation.put(r.getLocation(), shop);
+ shopLocation.put(l.getLocation(), shop);
+ } else {
+ plugin.debug("Added shop as single chest. (#" + shop.getID() + ")");
+
+ shopLocation.put(shop.getLocation(), shop);
+ }
+
+ if (addToDatabase) {
+ if (shop.getShopType() != ShopType.ADMIN) {
+ playerShopAmount.compute(shop.getVendor().getUniqueId(), (uuid, amount) -> amount == null ? new Counter(1) : amount.increment());
+ }
+ plugin.getShopDatabase().addShop(shop, callback);
+ } else {
+ if (callback != null) callback.callSyncResult(shop.getID());
+ }
+
+ }
+
+ /**
+ * Add a shop
+ * @param shop Shop to add
+ * @param addToDatabase Whether the shop should also be added to the database
+ */
+ public void addShop(Shop shop, boolean addToDatabase) {
+ addShop(shop, addToDatabase, null);
+ }
+
+ /**
+ * Removes (i.e. unloads) all currently loaded shops
+ */
+ public void removeShops() {
+ shopLocation.forEach((location, shop) -> {
+ if (!shop.isCreated()) return;
+
+ plugin.debug("Removing shop " + shop.getID());
+ shop.removeItem();
+ shop.removeHologram();
+ });
+ shopLocation.clear();
+ }
+
+ /** Remove a shop. May not work properly if double chest doesn't exist!
+ * @param shop Shop to remove
+ * @param removeFromDatabase Whether the shop should also be removed from the database
+ * @param callback Callback that - if succeeded - returns null
+ * @see ShopUtils#removeShopById(int, boolean, Callback)
+ */
+ public void removeShop(Shop shop, boolean removeFromDatabase, Callback callback) {
+ plugin.debug("Removing shop (#" + shop.getID() + ")");
+
+ if (shop.isCreated()) {
+ InventoryHolder ih = shop.getInventoryHolder();
+
+ if (ih instanceof DoubleChest) {
+ DoubleChest dc = (DoubleChest) ih;
+ Chest r = (Chest) dc.getRightSide();
+ Chest l = (Chest) dc.getLeftSide();
+
+ shopLocation.remove(r.getLocation());
+ shopLocation.remove(l.getLocation());
+ } else {
+ shopLocation.remove(shop.getLocation());
+ }
+
+ shop.removeItem();
+ shop.removeHologram();
+ }
+
+ if (removeFromDatabase) {
+ if (shop.getShopType() != ShopType.ADMIN) {
+ playerShopAmount.compute(shop.getVendor().getUniqueId(), (uuid, amount) -> amount == null ? new Counter() : amount.decrement());
+ }
+ plugin.getShopDatabase().removeShop(shop, callback);
+ } else {
+ if (callback != null) callback.callSyncResult(null);
+ }
+ }
+
+ /**
+ * Remove a shop. May not work properly if double chest doesn't exist!
+ * @param shop Shop to remove
+ * @param removeFromDatabase Whether the shop should also be removed from the database
+ * @see ShopUtils#removeShopById(int, boolean)
+ */
+ public void removeShop(Shop shop, boolean removeFromDatabase) {
+ removeShop(shop, removeFromDatabase, null);
+ }
+
+ /**
+ * Remove a shop by its ID
+ * @param shopId ID of the shop to remove
+ * @param removeFromDatabase Whether the shop should also be removed from the database
+ * @param callback Callback that - if succeeded - returns null
+ */
+ public void removeShopById(int shopId, boolean removeFromDatabase, Callback callback) {
+ Map toRemove = shopLocation.entrySet().stream()
+ .filter(e -> e.getValue().getID() == shopId)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+ plugin.debug(String.format("Removing %d shop(s) with ID %d", toRemove.size(), shopId));
+
+ if (toRemove.isEmpty()) {
+ if (callback != null) callback.callSyncResult(null);
+ return;
+ }
+
+ toRemove.forEach((loc, shop) -> {
+ shopLocation.remove(loc);
+
+ shop.removeItem();
+ shop.removeHologram();
+ });
+
+ Shop first = toRemove.values().iterator().next();
+ boolean isAdmin = first.getShopType() == ShopType.ADMIN;
+ UUID vendorUuid = first.getVendor().getUniqueId();
+
+ // Database#removeShop removes shop by ID so this only needs to be called once
+ if (removeFromDatabase) {
+ if (!isAdmin) {
+ playerShopAmount.compute(vendorUuid, (uuid, amount) -> amount == null ? new Counter() : amount.decrement());
+ }
+ plugin.getShopDatabase().removeShop(toRemove.values().iterator().next(), callback);
+ } else {
+ if (callback != null) callback.callSyncResult(null);
+ }
+ }
+
+ /**
+ * Remove a shop by its ID
+ * @param shopId ID of the shop to remove
+ * @param removeFromDatabase Whether the shop should also be removed from the database
+ */
+ public void removeShopById(int shopId, boolean removeFromDatabase) {
+ removeShopById(shopId, removeFromDatabase, null);
+ }
+
+ /**
+ * Get the shop limits of a player
+ * @param p Player, whose shop limits should be returned
+ * @return The shop limits of the given player
+ */
+ public int getShopLimit(Player p) {
+ int limit = 0;
+ boolean useDefault = true;
+
+ for (PermissionAttachmentInfo permInfo : p.getEffectivePermissions()) {
+ if (permInfo.getPermission().startsWith("shopchest.limit.") && p.hasPermission(permInfo.getPermission())) {
+ if (permInfo.getPermission().equalsIgnoreCase(Permissions.NO_LIMIT)) {
+ limit = -1;
+ useDefault = false;
+ break;
+ } else {
+ String[] spl = permInfo.getPermission().split("shopchest.limit.");
+
+ if (spl.length > 1) {
+ try {
+ int newLimit = Integer.valueOf(spl[1]);
+
+ if (newLimit < 0) {
+ limit = -1;
+ break;
+ }
+
+ limit = Math.max(limit, newLimit);
+ useDefault = false;
+ } catch (NumberFormatException ignored) {
+ /* Ignore and continue */
+ }
+ }
+ }
+ }
+ }
+
+ if (limit < -1) limit = -1;
+ return (useDefault ?Config.defaultLimit : limit);
+ }
+
+ /**
+ * Get the amount of shops of a player
+ * @param p Player, whose shops should be counted
+ * @return The amount of a shops a player has (if {@link Config#excludeAdminShops} is true, admin shops won't be counted)
+ */
+ public int getShopAmount(OfflinePlayer p) {
+ return playerShopAmount.getOrDefault(p.getUniqueId(), new Counter()).get();
+ }
+
+ /**
+ * Get all shops of a player from the database without loading them
+ * @param p Player, whose shops should be get
+ * @param callback Callback that returns a collection of the given player's shops
+ */
+ public void getShops(OfflinePlayer p, Callback> callback) {
+ plugin.getShopDatabase().getShops(p.getUniqueId(), new Callback>(plugin) {
+ @Override
+ public void onResult(Collection result) {
+ Set shops = new HashSet<>();
+ for (Shop playerShop : result) {
+ Shop loadedShop = getShop(playerShop.getLocation());
+ if (loadedShop != null && loadedShop.equals(playerShop)) {
+ shops.add(loadedShop);
+ } else {
+ shops.add(playerShop);
+ }
+ }
+ if (callback != null) callback.onResult(shops);
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ if (callback != null) callback.onError(throwable);
+ }
+ });
+ }
+
+ /**
+ * Loads the amount of shops for each player
+ * @param callback Callback that returns the amount of shops for each player
+ */
+ public void loadShopAmounts(final Callback