From 9b28423b06a908993b4f43dfed5188fa39dc6284 Mon Sep 17 00:00:00 2001 From: sandtechnology Date: Mon, 6 Apr 2020 13:40:16 +0800 Subject: [PATCH] Moving ItemMatcher Former-commit-id: d275c67568720a1614b22128267e9e3a748fcf03 --- .../Item/QuickShopItemMatcherImpl.java | 495 +++++++++++++++++- 1 file changed, 493 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/maxgamer/quickshop/Util/Matcher/Item/QuickShopItemMatcherImpl.java b/src/main/java/org/maxgamer/quickshop/Util/Matcher/Item/QuickShopItemMatcherImpl.java index afcecf381..c7a8ffce5 100644 --- a/src/main/java/org/maxgamer/quickshop/Util/Matcher/Item/QuickShopItemMatcherImpl.java +++ b/src/main/java/org/maxgamer/quickshop/Util/Matcher/Item/QuickShopItemMatcherImpl.java @@ -19,6 +19,15 @@ import java.util.*; @AllArgsConstructor public class QuickShopItemMatcherImpl implements ItemMatcher { private QuickShop plugin; + + private ItemMetaMatcher itemMetaMatcher; + + + public QuickShopItemMatcherImpl() { + plugin = QuickShop.getInstance(); + itemMetaMatcher=new ItemMetaMatcher(plugin.getConfig().getConfigurationSection("matcher.item")); + } + /** * Gets the ItemMatcher provider name * @@ -47,9 +56,491 @@ public class QuickShopItemMatcherImpl implements ItemMatcher { * @param givenStack The ItemStack will test matches with original itemstack. * @return The result of tests */ - @Override - public boolean matches(@Nullable ItemStack requireStack, @Nullable ItemStack givenStack) { + public boolean matches(@Nullable ItemStack[] requireStack, @Nullable ItemStack[] givenStack) { + if (requireStack == null && givenStack == null) { + return true; + } + if (requireStack == null || givenStack == null) { + return false; + } + + if (requireStack.length != givenStack.length) { + return false; + } + //For performance, we just check really equals in each index,check isn't contain or match will cost n^n time in most + for (int i = 0; i < requireStack.length; i++) { + //IDEA bug, ignore NPE tips + //noinspection ConstantConditions + if ((requireStack[i] != null) && (givenStack[i] != null) && + (requireStack[i].getAmount() != givenStack[i].getAmount())) { + return false; + } + + if (!matches(requireStack[i], givenStack[i])) { + return false; + } + } + return true; } + /** + * Compares two items to each other. Returns true if they match. Rewrite it to use more faster + * hashCode. + * + * @param requireStack The first item stack + * @param givenStack The second item stack + * @return true if the itemstacks match. (Material, durability, enchants, name) + */ + public boolean matches(@Nullable ItemStack requireStack, @Nullable ItemStack givenStack) { + if (requireStack == null && givenStack == null) { + return true; + } + + if (requireStack == null || givenStack == null) { + Util.debugLog( + "Match failed: A stack is null: " + + "requireStack[" + + requireStack + + "] givenStack[" + + givenStack + + "]"); + return false; // One of them is null (Can't be both, see above) + } + + requireStack = requireStack.clone(); + requireStack.setAmount(1); + givenStack = givenStack.clone(); + givenStack.setAmount(1); + switch (plugin.getConfig().getInt("matcher.work-type")) { + case 1: + return requireStack.isSimilar(givenStack); + case 2: + return requireStack.equals(givenStack); + } + + if (!typeMatches(requireStack, givenStack)) { + return false; + } + + // if (requireStack.hasItemMeta() != givenStack.hasItemMeta()) { + // Util.debugLog("Meta not matched"); + // return false; + + if (requireStack.hasItemMeta()) { + if (!givenStack.hasItemMeta()) { + return false; + } + return itemMetaMatcher.matches(requireStack, givenStack); + } + + return true; + } + + private boolean typeMatches(ItemStack requireStack, ItemStack givenStack) { + return requireStack.getType().equals(givenStack.getType()); + } + + private static class ItemMetaMatcher { + + interface Matcher { + + boolean match(ItemMeta meta1, ItemMeta meta2); + + } + + private final List matcherList = new LinkedList<>(); + + + private void addIfEnable(ConfigurationSection itemMatcherConfig, String path, Matcher matcher) { + if (itemMatcherConfig.getBoolean(path)) { + matcherList.add(matcher); + } + } + + public ItemMetaMatcher(ConfigurationSection itemMatcherConfig) { + + addIfEnable(itemMatcherConfig, "damage", (meta1, meta2) -> { + // if (!(meta1 instanceof Damageable)) { + // return true; //No damage need to check. + // } + // if(!(meta2 instanceof Damageable)){ + // return false; + // } + try { + Damageable damage1 = (Damageable) meta1; + Damageable damage2 = (Damageable) meta2; + // Check them damages, if givenDamage >= requireDamage, allow it. + return damage2.getDamage() <= damage1.getDamage(); + } catch (Throwable th) { + th.printStackTrace(); + return true; + } + }); + addIfEnable(itemMatcherConfig, "repaircost", (meta1, meta2) -> { + if (!(meta1 instanceof Repairable)) { + return true; + } + if (!(meta2 instanceof Repairable)) { + return false; + } + Repairable repairable1 = (Repairable) meta1; + Repairable repairable2 = (Repairable) meta2; + if (repairable1.hasRepairCost() != repairable2.hasRepairCost()) { + return false; + } + if (repairable1.hasRepairCost()) { + return repairable2.getRepairCost() <= repairable1.getRepairCost(); + } + return true; + }); + addIfEnable(itemMatcherConfig, "displayname", ((meta1, meta2) -> { + if (!meta1.hasDisplayName()) { + return true; + } else { + if (!meta2.hasDisplayName()) { + return false; + } + return meta1.getDisplayName().equals(meta2.getDisplayName()); + } + })); + // We didn't touch the loresMatches because many plugin use this check item. + addIfEnable(itemMatcherConfig, "lores", ((meta1, meta2) -> { + if (meta1.hasLore() != meta2.hasLore()) { + return false; + } + if (!meta1.hasLore()) { + return true; // No lores need to be checked. + } + List lores1 = meta1.getLore(); + List lores2 = meta2.getLore(); + return Arrays.deepEquals( + Objects.requireNonNull(lores1).toArray(), Objects.requireNonNull(lores2).toArray()); + + })); + addIfEnable(itemMatcherConfig, "enchs", ((meta1, meta2) -> { + if (meta1.hasEnchants()) { + if (!meta2.hasEnchants()) { + return false; + } + Map enchMap1 = meta1.getEnchants(); + Map enchMap2 = meta2.getEnchants(); + if (!Util.mapMatches(enchMap1, enchMap2)) { + return false; + } + } + if ((meta1 instanceof EnchantmentStorageMeta)) { + if (!(meta2 instanceof EnchantmentStorageMeta)) { + return false; + } + Map stor1 = ((EnchantmentStorageMeta) meta1).getStoredEnchants(); + Map stor2 = ((EnchantmentStorageMeta) meta2).getStoredEnchants(); + return Util.mapMatches(stor1, stor2); + } + return true; + })); + addIfEnable(itemMatcherConfig, "potions", ((meta1, meta2) -> { + if ((meta1 instanceof PotionMeta) != (meta2 instanceof PotionMeta)) { + return false; + } + + if (!(meta1 instanceof PotionMeta)) { + return true; // No potion meta needs to be checked. + } + + PotionMeta potion1 = (PotionMeta) meta1; + PotionMeta potion2 = (PotionMeta) meta2; + + if (potion1.hasColor()) { + if (!potion2.hasColor()) { + return false; + } else { + if (!Objects.requireNonNull(potion1.getColor()).equals(potion2.getColor())) { + return false; + } + } + } + if (potion1.hasCustomEffects()) { + if (!potion2.hasCustomEffects()) { + return false; + } + if (!Arrays.deepEquals( + potion1.getCustomEffects().toArray(), potion2.getCustomEffects().toArray())) { + return false; + } + // if + // (!Util.listMatches(potion1.getCustomEffects(),potion2.getCustomEffects())) { + // return false; + // } + } + PotionData data1 = potion1.getBasePotionData(); + PotionData data2 = potion2.getBasePotionData(); + if (data2.equals(data1)) { + return true; + } + if (!data2.getType().equals(data1.getType())) { + return false; + } + if (data1.isExtended()) { + if (!data2.isExtended()) { + return false; + } + } + if (data1.isUpgraded()) { + //noinspection RedundantIfStatement + if (!data2.isUpgraded()) { + return false; + } + } + return true; + })); + addIfEnable(itemMatcherConfig, "attributes", (meta1, meta2) -> { // requireStack doen't need require must have AM, skipping.. + if (!meta1.hasAttributeModifiers()) { + return true; + } else { + // If require AM but hadn't, the item not matched. + if (!meta2.hasAttributeModifiers()) { + return false; + } + Set set1 = Objects.requireNonNull(meta1.getAttributeModifiers()).keySet(); + Set set2 = Objects.requireNonNull(meta2.getAttributeModifiers()).keySet(); + for (Attribute att : set1) { + if (!set2.contains(att)) { + return false; + } else if (!meta1 + .getAttributeModifiers() + .get(att) + .equals(meta2.getAttributeModifiers().get(att))) { + return false; + } + } + return true; + } + }); + addIfEnable(itemMatcherConfig, "itemflags", ((meta1, meta2) -> { + if (meta1.getItemFlags().isEmpty()) { + return true; + } else { + if (meta2.getItemFlags().isEmpty()) { + return false; + } + return Arrays.deepEquals(meta1.getItemFlags().toArray(), meta2.getItemFlags().toArray()); + } + })); + addIfEnable(itemMatcherConfig, "custommodeldata", ((meta1, meta2) -> { + try { + if (!meta1.hasCustomModelData()) { + return true; + } else { + if (!meta2.hasCustomModelData()) { + return false; + } + return meta1.getCustomModelData() == meta2.getCustomModelData(); + } + } catch (NoSuchMethodError err) { + // Ignore, for 1.13 compatibility + return true; + } + })); + addIfEnable(itemMatcherConfig, "book", ((meta1, meta2) -> { + if (!(meta1 instanceof BookMeta)) { + return true; + } + if (!(meta2 instanceof BookMeta)) { + return false; + } + BookMeta book1 = (BookMeta) meta1; + BookMeta book2 = (BookMeta) meta2; + if (book1.hasTitle()) { + if (!book2.hasTitle()) { + return false; + } + if (!Objects.equals(book1.getTitle(), book2.getTitle())) { + return false; + } + } + if (book1.hasPages()) { + if (!book2.hasPages()) { + return false; + } + if (!book1.getPages().equals(book2.getPages())) { + return false; + } + } + if (book1.hasAuthor()) { + if (!book2.hasAuthor()) { + return false; + } + if (!Objects.equals(book1.getAuthor(), book2.getAuthor())) { + return false; + } + } + if (book1.hasGeneration()) { + if (!book2.hasGeneration()) { + return false; + } + return Objects.equals(book1.getGeneration(), book2.getGeneration()); + } + return true; + })); + addIfEnable(itemMatcherConfig, "banner", ((meta1, meta2) -> { + if ((meta1 instanceof BannerMeta) != (meta2 instanceof BannerMeta)) { + return false; + } + if (!(meta1 instanceof BannerMeta)) { + return true; + } + BannerMeta bannerMeta1 = (BannerMeta) meta1; + BannerMeta bannerMeta2 = (BannerMeta) meta2; + if (bannerMeta1.numberOfPatterns() != bannerMeta2.numberOfPatterns()) { + return false; + } + return Util.listMatches(bannerMeta1.getPatterns(), bannerMeta2.getPatterns()); + })); + addIfEnable(itemMatcherConfig, "skull", (meta1, meta2) -> { + if ((meta1 instanceof SkullMeta) != (meta2 instanceof SkullMeta)) { + return false; + } + if (!(meta1 instanceof SkullMeta)) { + return true; + } + //getOwningPlayer will let server query playerProfile in server thread + //Causing huge lag, so using String instead + String player1 = ((SkullMeta) meta1).getOwner(); + String player2 = ((SkullMeta) meta2).getOwner(); + if (player1 == null) { + return true; + } + return player1.equalsIgnoreCase(player2); + }); + addIfEnable(itemMatcherConfig, "map", (meta1, meta2) -> { + if ((meta1 instanceof MapMeta) != (meta2 instanceof MapMeta)) { + return false; + } + if (!(meta1 instanceof MapMeta)) { + return true; + } + MapMeta mapMeta1 = ((MapMeta) meta1); + MapMeta mapMeta2 = ((MapMeta) meta2); + if (!mapMeta1.hasMapView() || mapMeta1.getMapView() == null) { + return true; + } + if (!mapMeta1.getMapView().equals(mapMeta2.getMapView())) { + return false; + } + + if (!mapMeta1.hasColor() || mapMeta1.getColor() == null) { + return true; + } + if (!mapMeta1.getColor().equals(mapMeta2.getColor())) { + return false; + } + + if (!mapMeta1.hasLocationName() || mapMeta1.getLocationName() == null) { + return true; + } + return mapMeta1.getLocationName().equals(mapMeta2.getLocationName()); + }); + addIfEnable(itemMatcherConfig, "firework", (meta1, meta2) -> { + if ((meta1 instanceof FireworkMeta) != (meta2 instanceof FireworkMeta)) { + return false; + } + if (!(meta1 instanceof FireworkMeta)) { + return true; + } + FireworkMeta fireworkMeta1 = ((FireworkMeta) meta1); + FireworkMeta fireworkMeta2 = ((FireworkMeta) meta2); + if (!fireworkMeta1.hasEffects()) { + return true; + } + if (!fireworkMeta1.getEffects().equals(fireworkMeta2.getEffects())) { + return false; + } + return fireworkMeta1.getPower() == fireworkMeta2.getPower(); + }); + addIfEnable(itemMatcherConfig, "leatherArmor", ((meta1, meta2) -> { + if ((meta1 instanceof LeatherArmorMeta) != (meta2 instanceof LeatherArmorMeta)) { + return false; + } + if (!(meta1 instanceof LeatherArmorMeta)) { + return true; + } + return ((LeatherArmorMeta) meta1).getColor().equals(((LeatherArmorMeta) meta2).getColor()); + })); + addIfEnable(itemMatcherConfig, "fishBucket", (meta1, meta2) -> { + if ((meta1 instanceof TropicalFishBucketMeta) != (meta2 instanceof TropicalFishBucketMeta)) { + return false; + } + if (!(meta1 instanceof TropicalFishBucketMeta)) { + return true; + } + TropicalFishBucketMeta fishBucketMeta1 = ((TropicalFishBucketMeta) meta1); + TropicalFishBucketMeta fishBucketMeta2 = ((TropicalFishBucketMeta) meta2); + if (!fishBucketMeta1.hasVariant()) { + return true; + } + + return fishBucketMeta2.hasVariant() && fishBucketMeta1.getPattern() == fishBucketMeta2.getPattern() + && fishBucketMeta1.getBodyColor().equals(fishBucketMeta2.getBodyColor()) + && fishBucketMeta1.getPatternColor().equals(fishBucketMeta2.getPatternColor()); + }); + addIfEnable(itemMatcherConfig, "suspiciousStew", ((meta1, meta2) -> { + if ((meta1 instanceof SuspiciousStewMeta) != (meta2 instanceof SuspiciousStewMeta)) { + return false; + } + if (!(meta1 instanceof SuspiciousStewMeta)) { + return true; + } + SuspiciousStewMeta stewMeta1 = ((SuspiciousStewMeta) meta1); + SuspiciousStewMeta stewMeta2 = ((SuspiciousStewMeta) meta2); + if (!stewMeta1.hasCustomEffects()) { + return true; + } + return stewMeta1.getCustomEffects().equals(stewMeta2.getCustomEffects()); + })); + addIfEnable(itemMatcherConfig, "shulkerBox", ((meta1, meta2) -> { + //https://www.spigotmc.org/threads/getting-the-inventory-of-a-shulker-box-itemstack.212369 + if ((meta1 instanceof BlockStateMeta) != (meta2 instanceof BlockStateMeta)) { + return false; + } + if (!(meta1 instanceof BlockStateMeta)) { + return true; + } + + if ((((BlockStateMeta) meta1).getBlockState() instanceof ShulkerBox) != ((BlockStateMeta) meta2).getBlockState() instanceof ShulkerBox) { + return false; + } + if (!(((BlockStateMeta) meta1).getBlockState() instanceof ShulkerBox)) { + return true; + } + return QuickShop.getInstance().getItemMatcher().matches(((ShulkerBox) ((BlockStateMeta) meta1).getBlockState()).getInventory().getContents(), ((ShulkerBox) ((BlockStateMeta) meta2).getBlockState()).getInventory().getContents()); + })); + + } + + + boolean matches(ItemStack requireStack, ItemStack givenStack) { + if (requireStack.hasItemMeta() != givenStack.hasItemMeta()) { + return false; + } + if (!requireStack.hasItemMeta()) { + return true; // Passed check. no meta need to check. + } + ItemMeta meta1 = requireStack.getItemMeta(); + ItemMeta meta2 = givenStack.getItemMeta(); + for (Matcher matcher : matcherList) { + if (!matcher.match(meta1, meta2)) { + return false; + } + } + return true; + } + + + private boolean rootMatches(ItemMeta meta1, ItemMeta meta2) { + return (meta1.hashCode() == meta2.hashCode()); + } + + + } } \ No newline at end of file