diff --git a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/NoteBlockMusic.java b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/NoteBlockMusic.java index 2eda62302..948c39b04 100644 --- a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/NoteBlockMusic.java +++ b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/NoteBlockMusic.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2022 Crypto Morin + * Copyright (c) 2023 Crypto Morin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,7 @@ /** * NoteBlockMusic - Write music scripts for Minecraft.
- * You can write small text scripts for Minecraft note blocks + * You can write small text scripts for Minecraft note blocks * without needing to use any redstone or building to make your music. * This class is independent of XSound. * @@ -128,7 +128,7 @@ public static XSound getSoundFromInstrument(@NotNull Instrument instrument) { * The character paseed to this method is assumed to be uppercase, * otherwise it needs to be {@code ch & 0x5f} manually. *

- * https://minecraft.gamepedia.com/Note_Block#Notes + * https://minecraft.wiki/w/Note_Block#Notes * * @param ch the character of the note tone. * @return the note tone or null if not found. diff --git a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XBlock.java b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XBlock.java index c57448a9f..e50b3a669 100644 --- a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XBlock.java +++ b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XBlock.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2022 Crypto Morin + * Copyright (c) 2023 Crypto Morin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XItemStack.java b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XItemStack.java index 3f236ae4f..766855b12 100644 --- a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XItemStack.java +++ b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XItemStack.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2021 Crypto Morin + * Copyright (c) 2023 Crypto Morin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,6 +43,9 @@ import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.*; +import org.bukkit.inventory.meta.trim.ArmorTrim; +import org.bukkit.inventory.meta.trim.TrimMaterial; +import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.map.MapView; import org.bukkit.material.MaterialData; import org.bukkit.material.SpawnEgg; @@ -71,10 +74,14 @@ * ConfigurationSection section = plugin.getConfig().getConfigurationSection("staffs.dragon-staff"); * ItemStack item = XItemStack.deserialize(section); * - * ItemStack: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/inventory/ItemStack.html + * ItemStack * * @author Crypto Morin - * @version 6.0.0 + * @version 7.3.3 + * @see XMaterial + * @see XPotion + * @see SkullUtils + * @see XEnchantment * @see ItemStack */ @SuppressWarnings("ALL") @@ -93,6 +100,22 @@ public static boolean isDefaultItem(ItemStack item) { return DEFAULT_MATERIAL.isSimilar(item); } + private static BlockState safeBlockState(BlockStateMeta meta) { + // Due to a bug in the latest paper v1.9-1.10 (and some older v1.11) versions. + // java.lang.IllegalStateException: Missing blockState for BREWING_STAND_ITEM + // BREWING_STAND_ITEM, ENCHANTMENT_TABLE, REDSTONE_COMPARATOR + // https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/diff/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java?until=b6ad714e853042def52620befe9bc85d0137cd71 + try { + return meta.getBlockState(); + } catch (IllegalStateException ex) { + if (ex.getMessage().toLowerCase(Locale.ENGLISH).contains("missing blockstate")) { + return null; + } else { + throw ex; + } + } + } + /** * Writes an ItemStack object into a config. * The config file will not save after the object is written. @@ -150,7 +173,7 @@ public static void serialize(@NotNull ItemStack item, @NotNull ConfigurationSect config.set("flags", flagNames); } - // Attributes - https://minecraft.gamepedia.com/Attribute + // Attributes - https://minecraft.wiki/w/Attribute if (supports(13)) { Multimap attributes = meta.getAttributeModifiers(); if (attributes != null) { @@ -168,7 +191,7 @@ public static void serialize(@NotNull ItemStack item, @NotNull ConfigurationSect } if (meta instanceof BlockStateMeta) { - BlockState state = ((BlockStateMeta) meta).getBlockState(); + BlockState state = safeBlockState((BlockStateMeta) meta); if (supports(11) && state instanceof ShulkerBox) { ShulkerBox box = (ShulkerBox) state; @@ -180,7 +203,7 @@ public static void serialize(@NotNull ItemStack item, @NotNull ConfigurationSect } } else if (state instanceof CreatureSpawner) { CreatureSpawner cs = (CreatureSpawner) state; - config.set("spawner", cs.getSpawnedType().name()); + if (cs.getSpawnedType() != null) config.set("spawner", cs.getSpawnedType().name()); } } else if (meta instanceof EnchantmentStorageMeta) { EnchantmentStorageMeta book = (EnchantmentStorageMeta) meta; @@ -210,7 +233,7 @@ public static void serialize(@NotNull ItemStack item, @NotNull ConfigurationSect effects.add(effect.getType().getName() + ", " + effect.getDuration() + ", " + effect.getAmplifier()); } - config.set("effects", effects); + if (!effects.isEmpty()) config.set("effects", effects); PotionData potionData = potion.getBasePotionData(); config.set("base-effect", potionData.getType().name() + ", " + potionData.isExtended() + ", " + potionData.isUpgraded()); @@ -295,60 +318,80 @@ public static void serialize(@NotNull ItemStack item, @NotNull ConfigurationSect view.set("unlimited-tracking", mapView.isUnlimitedTracking()); } } - } else if (supports(17)) { - if (meta instanceof AxolotlBucketMeta) { - AxolotlBucketMeta bucket = (AxolotlBucketMeta) meta; - if (bucket.hasVariant()) config.set("color", bucket.getVariant().toString()); - } - } else if (supports(16)) { - if (meta instanceof CompassMeta) { - CompassMeta compass = (CompassMeta) meta; - ConfigurationSection subSection = config.createSection("lodestone"); - subSection.set("tracked", compass.isLodestoneTracked()); - if (compass.hasLodestone()) { - Location location = compass.getLodestone(); - subSection.set("location.world", location.getWorld().getName()); - subSection.set("location.x", location.getX()); - subSection.set("location.y", location.getY()); - subSection.set("location.z", location.getZ()); + } else { + if (supports(20)) { + if (meta instanceof ArmorMeta) { + ArmorMeta armorMeta = (ArmorMeta) meta; + if (armorMeta.hasTrim()) { + ArmorTrim trim = armorMeta.getTrim(); + ConfigurationSection trimConfig = config.createSection("trim"); + trimConfig.set("material", trim.getMaterial().getKey().getNamespace() + ':' + trim.getMaterial().getKey().getKey()); + trimConfig.set("pattern", trim.getPattern().getKey().getNamespace() + ':' + trim.getPattern().getKey().getKey()); + } } } - } else if (supports(14)) { - if (meta instanceof CrossbowMeta) { - CrossbowMeta crossbow = (CrossbowMeta) meta; - int i = 0; - for (ItemStack projectiles : crossbow.getChargedProjectiles()) { - serialize(projectiles, config.getConfigurationSection("projectiles." + i)); - i++; - } - } else if (meta instanceof TropicalFishBucketMeta) { - TropicalFishBucketMeta tropical = (TropicalFishBucketMeta) meta; - config.set("pattern", tropical.getPattern().name()); - config.set("color", tropical.getBodyColor().name()); - config.set("pattern-color", tropical.getPatternColor().name()); - } else if (meta instanceof SuspiciousStewMeta) { - SuspiciousStewMeta stew = (SuspiciousStewMeta) meta; - List customEffects = stew.getCustomEffects(); - List effects = new ArrayList<>(customEffects.size()); - for (PotionEffect effect : customEffects) { - effects.add(effect.getType().getName() + ", " + effect.getDuration() + ", " + effect.getAmplifier()); + if (supports(17)) { + if (meta instanceof AxolotlBucketMeta) { + AxolotlBucketMeta bucket = (AxolotlBucketMeta) meta; + if (bucket.hasVariant()) config.set("color", bucket.getVariant().toString()); } + } - config.set("effects", effects); + if (supports(16)) { + if (meta instanceof CompassMeta) { + CompassMeta compass = (CompassMeta) meta; + ConfigurationSection subSection = config.createSection("lodestone"); + subSection.set("tracked", compass.isLodestoneTracked()); + if (compass.hasLodestone()) { + Location location = compass.getLodestone(); + subSection.set("location.world", location.getWorld().getName()); + subSection.set("location.x", location.getX()); + subSection.set("location.y", location.getY()); + subSection.set("location.z", location.getZ()); + } + } } - } else if (!supports(13)) { - // Spawn Eggs - if (supports(11)) { - if (meta instanceof SpawnEggMeta) { - SpawnEggMeta spawnEgg = (SpawnEggMeta) meta; - config.set("creature", spawnEgg.getSpawnedType().getName()); + + if (supports(14)) { + if (meta instanceof CrossbowMeta) { + CrossbowMeta crossbow = (CrossbowMeta) meta; + int i = 0; + for (ItemStack projectiles : crossbow.getChargedProjectiles()) { + serialize(projectiles, config.getConfigurationSection("projectiles." + i)); + i++; + } + } else if (meta instanceof TropicalFishBucketMeta) { + TropicalFishBucketMeta tropical = (TropicalFishBucketMeta) meta; + config.set("pattern", tropical.getPattern().name()); + config.set("color", tropical.getBodyColor().name()); + config.set("pattern-color", tropical.getPatternColor().name()); + } else if (meta instanceof SuspiciousStewMeta) { + SuspiciousStewMeta stew = (SuspiciousStewMeta) meta; + List customEffects = stew.getCustomEffects(); + List effects = new ArrayList<>(customEffects.size()); + + for (PotionEffect effect : customEffects) { + effects.add(effect.getType().getName() + ", " + effect.getDuration() + ", " + effect.getAmplifier()); + } + + config.set("effects", effects); } - } else { - MaterialData data = item.getData(); - if (data instanceof SpawnEgg) { - SpawnEgg spawnEgg = (SpawnEgg) data; - config.set("creature", spawnEgg.getSpawnedType().getName()); + } + + if (!supports(13)) { + // Spawn Eggs + if (supports(11)) { + if (meta instanceof SpawnEggMeta) { + SpawnEggMeta spawnEgg = (SpawnEggMeta) meta; + config.set("creature", spawnEgg.getSpawnedType().getName()); + } + } else { + MaterialData data = item.getData(); + if (data instanceof SpawnEgg) { + SpawnEgg spawnEgg = (SpawnEgg) data; + config.set("creature", spawnEgg.getSpawnedType().getName()); + } } } } @@ -533,14 +576,15 @@ public static ItemStack edit(@NotNull ItemStack item, if (unsupportedMaterialCondition.hasSolution()) material = unsupportedMaterialCondition.solution; else throw unsupportedMaterialCondition; } -// if (XMaterialUtil.INVENTORY_NOT_DISPLAYABLE.isTagged(material)) { -// UnAcceptableMaterialCondition unsupportedMaterialCondition = new UnAcceptableMaterialCondition(material, UnAcceptableMaterialCondition.Reason.NOT_DISPLAYABLE); -// if (restart == null) throw unsupportedMaterialCondition; -// restart.accept(unsupportedMaterialCondition); -// -// if (unsupportedMaterialCondition.hasSolution()) material = unsupportedMaterialCondition.solution; -// else throw unsupportedMaterialCondition; -// } + if (XMaterialUtil.INVENTORY_NOT_DISPLAYABLE.isTagged(material)) { + UnAcceptableMaterialCondition unsupportedMaterialCondition = new UnAcceptableMaterialCondition(material, UnAcceptableMaterialCondition.Reason.NOT_DISPLAYABLE); + if (restart == null) throw unsupportedMaterialCondition; + restart.accept(unsupportedMaterialCondition); + + if (unsupportedMaterialCondition.hasSolution()) material = unsupportedMaterialCondition.solution; + else throw unsupportedMaterialCondition; + } + material.setType(item); } @@ -580,7 +624,6 @@ public static ItemStack edit(@NotNull ItemStack item, BannerMeta banner = (BannerMeta) meta; ConfigurationSection patterns = config.getConfigurationSection("patterns"); - //System.out.println("patters v2: " + patterns); if (patterns != null) { for (String pattern : patterns.getKeys(false)) { PatternType type = PatternType.getByIdentifier(pattern); @@ -636,13 +679,17 @@ public static ItemStack edit(@NotNull ItemStack item, } } else if (meta instanceof BlockStateMeta) { BlockStateMeta bsm = (BlockStateMeta) meta; - BlockState state = bsm.getBlockState(); + BlockState state = safeBlockState(bsm); if (state instanceof CreatureSpawner) { + // Do we still need this? XMaterial handles it, doesn't it? CreatureSpawner spawner = (CreatureSpawner) state; - spawner.setSpawnedType(Enums.getIfPresent(EntityType.class, config.getString("spawner").toUpperCase(Locale.ENGLISH)).orNull()); - spawner.update(true); - bsm.setBlockState(spawner); + String spawnerStr = config.getString("spawner"); + if (!Strings.isNullOrEmpty(spawnerStr)) { + spawner.setSpawnedType(Enums.getIfPresent(EntityType.class, spawnerStr.toUpperCase(Locale.ENGLISH)).orNull()); + spawner.update(true); + bsm.setBlockState(spawner); + } } else if (supports(11) && state instanceof ShulkerBox) { ConfigurationSection shulkerSection = config.getConfigurationSection("contents"); if (shulkerSection != null) { @@ -663,7 +710,6 @@ public static ItemStack edit(@NotNull ItemStack item, banner.setBaseColor(DyeColor.WHITE); } - //System.out.println("patterns are " + patterns); if (patterns != null) { for (String pattern : patterns.getKeys(false)) { PatternType type = PatternType.getByIdentifier(pattern); @@ -695,15 +741,17 @@ public static ItemStack edit(@NotNull ItemStack item, .or(FireworkEffect.Type.STAR)); ConfigurationSection colorsSection = fw.getConfigurationSection("colors"); - List fwColors = colorsSection.getStringList("base"); - List colors = new ArrayList<>(fwColors.size()); - for (String colorStr : fwColors) colors.add(parseColor(colorStr)); - builder.withColor(colors); - - fwColors = colorsSection.getStringList("fade"); - colors = new ArrayList<>(fwColors.size()); - for (String colorStr : fwColors) colors.add(parseColor(colorStr)); - builder.withFade(colors); + if (colorsSection != null) { + List fwColors = colorsSection.getStringList("base"); + List colors = new ArrayList<>(fwColors.size()); + for (String colorStr : fwColors) colors.add(parseColor(colorStr)); + builder.withColor(colors); + + fwColors = colorsSection.getStringList("fade"); + colors = new ArrayList<>(fwColors.size()); + for (String colorStr : fwColors) colors.add(parseColor(colorStr)); + builder.withFade(colors); + } firework.addEffect(builder.build()); } @@ -752,83 +800,110 @@ public static ItemStack edit(@NotNull ItemStack item, mapView.setUnlimitedTracking(view.getBoolean("unlimited-tracking")); ConfigurationSection centerSection = view.getConfigurationSection("center"); - mapView.setCenterX(centerSection.getInt("x")); - mapView.setCenterZ(centerSection.getInt("z")); + if (centerSection != null) { + mapView.setCenterX(centerSection.getInt("x")); + mapView.setCenterZ(centerSection.getInt("z")); + } map.setMapView(mapView); } } } } - } else if (supports(17)) { - if (meta instanceof AxolotlBucketMeta) { - AxolotlBucketMeta bucket = (AxolotlBucketMeta) meta; - String variantStr = config.getString("color"); - if (variantStr != null) { - Axolotl.Variant variant = Enums.getIfPresent(Axolotl.Variant.class, variantStr.toUpperCase(Locale.ENGLISH)).or(Axolotl.Variant.BLUE); - bucket.setVariant(variant); + } else { + if (supports(20)) { + if (meta instanceof ArmorMeta) { + ArmorMeta armorMeta = (ArmorMeta) meta; + if (config.contains("trim")) { + ConfigurationSection trim = config.getConfigurationSection("trim"); + TrimMaterial trimMaterial = Registry.TRIM_MATERIAL.get(NamespacedKey.fromString(trim.getString("material"))); + TrimPattern trimPattern = Registry.TRIM_PATTERN.get(NamespacedKey.fromString(trim.getString("pattern"))); + armorMeta.setTrim(new ArmorTrim(trimMaterial, trimPattern)); + } } } - } else if (supports(16)) { - if (meta instanceof CompassMeta) { - CompassMeta compass = (CompassMeta) meta; - compass.setLodestoneTracked(config.getBoolean("tracked")); - - ConfigurationSection lodestone = config.getConfigurationSection("lodestone"); - if (lodestone != null) { - World world = Bukkit.getWorld(lodestone.getString("world")); - double x = lodestone.getDouble("x"); - double y = lodestone.getDouble("y"); - double z = lodestone.getDouble("z"); - compass.setLodestone(new Location(world, x, y, z)); + + if (supports(17)) { + if (meta instanceof AxolotlBucketMeta) { + AxolotlBucketMeta bucket = (AxolotlBucketMeta) meta; + String variantStr = config.getString("color"); + if (variantStr != null) { + Axolotl.Variant variant = Enums.getIfPresent(Axolotl.Variant.class, variantStr.toUpperCase(Locale.ENGLISH)).or(Axolotl.Variant.BLUE); + bucket.setVariant(variant); + } } } - } else if (supports(15)) { - if (meta instanceof SuspiciousStewMeta) { - SuspiciousStewMeta stew = (SuspiciousStewMeta) meta; - for (String effects : config.getStringList("effects")) { - XPotion.Effect effect = XPotion.parseEffect(effects); - if (effect.hasChance()) stew.addCustomEffect(effect.getEffect(), true); + + if (supports(16)) { + if (meta instanceof CompassMeta) { + CompassMeta compass = (CompassMeta) meta; + compass.setLodestoneTracked(config.getBoolean("tracked")); + + ConfigurationSection lodestone = config.getConfigurationSection("lodestone"); + if (lodestone != null) { + World world = Bukkit.getWorld(lodestone.getString("world")); + double x = lodestone.getDouble("x"); + double y = lodestone.getDouble("y"); + double z = lodestone.getDouble("z"); + compass.setLodestone(new Location(world, x, y, z)); + } } } - } else if (supports(14)) { - if (meta instanceof CrossbowMeta) { - CrossbowMeta crossbow = (CrossbowMeta) meta; - for (String projectiles : config.getConfigurationSection("projectiles").getKeys(false)) { - ItemStack projectile = deserialize(config.getConfigurationSection("projectiles." + projectiles)); - crossbow.addChargedProjectile(projectile); + + if (supports(15)) { + if (meta instanceof SuspiciousStewMeta) { + SuspiciousStewMeta stew = (SuspiciousStewMeta) meta; + for (String effects : config.getStringList("effects")) { + XPotion.Effect effect = XPotion.parseEffect(effects); + if (effect.hasChance()) stew.addCustomEffect(effect.getEffect(), true); + } } - } else if (meta instanceof TropicalFishBucketMeta) { - TropicalFishBucketMeta tropical = (TropicalFishBucketMeta) meta; - DyeColor color = Enums.getIfPresent(DyeColor.class, config.getString("color")).or(DyeColor.WHITE); - DyeColor patternColor = Enums.getIfPresent(DyeColor.class, config.getString("pattern-color")).or(DyeColor.WHITE); - TropicalFish.Pattern pattern = Enums.getIfPresent(TropicalFish.Pattern.class, config.getString("pattern")).or(TropicalFish.Pattern.BETTY); - - tropical.setBodyColor(color); - tropical.setPatternColor(patternColor); - tropical.setPattern(pattern); } - // Apparently Suspicious Stew was never added in 1.14 - } else if (!supports(13)) { - // Spawn Eggs - if (supports(11)) { - if (meta instanceof SpawnEggMeta) { - String creatureName = config.getString("creature"); - if (!Strings.isNullOrEmpty(creatureName)) { - SpawnEggMeta spawnEgg = (SpawnEggMeta) meta; - com.google.common.base.Optional creature = Enums.getIfPresent(EntityType.class, creatureName.toUpperCase(Locale.ENGLISH)); - if (creature.isPresent()) spawnEgg.setSpawnedType(creature.get()); + + if (supports(14)) { + if (meta instanceof CrossbowMeta) { + CrossbowMeta crossbow = (CrossbowMeta) meta; + ConfigurationSection projectiles = config.getConfigurationSection("projectiles"); + if (projectiles != null) { + for (String projectile : projectiles.getKeys(false)) { + ItemStack projectileItem = deserialize(config.getConfigurationSection("projectiles." + projectile)); + crossbow.addChargedProjectile(projectileItem); + } } + } else if (meta instanceof TropicalFishBucketMeta) { + TropicalFishBucketMeta tropical = (TropicalFishBucketMeta) meta; + DyeColor color = Enums.getIfPresent(DyeColor.class, config.getString("color")).or(DyeColor.WHITE); + DyeColor patternColor = Enums.getIfPresent(DyeColor.class, config.getString("pattern-color")).or(DyeColor.WHITE); + TropicalFish.Pattern pattern = Enums.getIfPresent(TropicalFish.Pattern.class, config.getString("pattern")).or(TropicalFish.Pattern.BETTY); + + tropical.setBodyColor(color); + tropical.setPatternColor(patternColor); + tropical.setPattern(pattern); } - } else { - MaterialData data = item.getData(); - if (data instanceof SpawnEgg) { - String creatureName = config.getString("creature"); - if (!Strings.isNullOrEmpty(creatureName)) { - SpawnEgg spawnEgg = (SpawnEgg) data; - com.google.common.base.Optional creature = Enums.getIfPresent(EntityType.class, creatureName.toUpperCase(Locale.ENGLISH)); - if (creature.isPresent()) spawnEgg.setSpawnedType(creature.get()); - item.setData(data); + } + + // Apparently Suspicious Stew was never added in 1.14 + if (!supports(13)) { + // Spawn Eggs + if (supports(11)) { + if (meta instanceof SpawnEggMeta) { + String creatureName = config.getString("creature"); + if (!Strings.isNullOrEmpty(creatureName)) { + SpawnEggMeta spawnEgg = (SpawnEggMeta) meta; + com.google.common.base.Optional creature = Enums.getIfPresent(EntityType.class, creatureName.toUpperCase(Locale.ENGLISH)); + if (creature.isPresent()) spawnEgg.setSpawnedType(creature.get()); + } + } + } else { + MaterialData data = item.getData(); + if (data instanceof SpawnEgg) { + String creatureName = config.getString("creature"); + if (!Strings.isNullOrEmpty(creatureName)) { + SpawnEgg spawnEgg = (SpawnEgg) data; + com.google.common.base.Optional creature = Enums.getIfPresent(EntityType.class, creatureName.toUpperCase(Locale.ENGLISH)); + if (creature.isPresent()) spawnEgg.setSpawnedType(creature.get()); + item.setData(data); + } } } } @@ -931,7 +1006,7 @@ public static ItemStack edit(@NotNull ItemStack item, meta.addItemFlags(ITEM_FLAGS); } - // Atrributes - https://minecraft.gamepedia.com/Attribute + // Atrributes - https://minecraft.wiki/w/Attribute if (supports(13)) { ConfigurationSection attributes = config.getConfigurationSection("attributes"); if (attributes != null) { @@ -948,7 +1023,7 @@ public static ItemStack edit(@NotNull ItemStack item, AttributeModifier modifier = new AttributeModifier( id, section.getString("name"), - section.getInt("amount"), + section.getDouble("amount"), Enums.getIfPresent(AttributeModifier.Operation.class, section.getString("operation")) .or(AttributeModifier.Operation.ADD_NUMBER), slot); @@ -1069,8 +1144,8 @@ public static List addItems(@NotNull Inventory inventory, boolean spl * CraftInventory * * @param inventory the inventory to add the items to. - * @param split if it should check for the inventory stack size {@link Inventory#getMaxStackSize()} or - * item's max stack size {@link ItemStack#getMaxStackSize()} when putting items. This is useful when + * @param split false if it should check for the inventory stack size {@link Inventory#getMaxStackSize()} or + * true for item's max stack size {@link ItemStack#getMaxStackSize()} when putting items. This is useful when * you're adding stacked tools such as swords that you'd like to split them to other slots. * @param modifiableSlots the slots that are allowed to be used for adding the items, otherwise null to allow all slots. * @param items the items to add. @@ -1086,7 +1161,7 @@ public static List addItems(@NotNull Inventory inventory, boolean spl List leftOvers = new ArrayList<>(items.length); // No other optimized way to access this using Bukkit API... - // We could pass the length to individual methods so they could also use getItem() which + // We could pass the length to individual methods, so they could also use getItem() which // skips parsing all the items in the inventory if not needed, but that's just too much. // Note: This is not the same as Inventory#getSize() int invSize = inventory.getStorageContents().length; @@ -1094,12 +1169,13 @@ public static List addItems(@NotNull Inventory inventory, boolean spl for (ItemStack item : items) { int lastPartial = 0; + int maxAmount = split ? item.getMaxStackSize() : inventory.getMaxStackSize(); while (true) { // Check if there is a similar item that can be stacked before using free slots. int firstPartial = lastPartial >= invSize ? -1 : firstPartial(inventory, item, lastPartial, modifiableSlots); - if (firstPartial == -1) { - // Start adding items to left overs if there are no partial and empty slots + if (firstPartial == -1) { // No partial items found + // Start adding items to leftovers if there are no partial and empty slots // -1 means that there are no empty slots left. if (lastEmpty != -1) lastEmpty = firstEmpty(inventory, lastEmpty, modifiableSlots); if (lastEmpty == -1) { @@ -1109,26 +1185,22 @@ public static List addItems(@NotNull Inventory inventory, boolean spl // Avoid firstPartial() for checking again for no reason, since if we're already checking // for free slots, that means there are no partials even left. - lastPartial = invSize + 1; + lastPartial = Integer.MAX_VALUE; - int maxSize = split ? item.getMaxStackSize() : inventory.getMaxStackSize(); int amount = item.getAmount(); - if (amount <= maxSize) { + if (amount <= maxAmount) { inventory.setItem(lastEmpty, item); break; } else { ItemStack copy = item.clone(); - copy.setAmount(maxSize); + copy.setAmount(maxAmount); inventory.setItem(lastEmpty, copy); - item.setAmount(amount - maxSize); + item.setAmount(amount - maxAmount); } if (++lastEmpty == invSize) lastEmpty = -1; } else { ItemStack partialItem = inventory.getItem(firstPartial); - int maxAmount = split ? partialItem.getMaxStackSize() : inventory.getMaxStackSize(); - int partialAmount = partialItem.getAmount(); - int amount = item.getAmount(); - int sum = amount + partialAmount; + int sum = item.getAmount() + partialItem.getAmount(); if (sum <= maxAmount) { partialItem.setAmount(sum); diff --git a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XMaterialUtil.java b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XMaterialUtil.java index f22b1b328..e46fa06ee 100644 --- a/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XMaterialUtil.java +++ b/platform/platform-bukkit/src/main/java/taboolib/library/xseries/XMaterialUtil.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2022 Crypto Morin + * Copyright (c) 2023 Crypto Morin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,11 +22,15 @@ package taboolib.library.xseries; import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import taboolib.common.Isolated; +import java.lang.reflect.Field; import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; @SuppressWarnings("CommentedOutCode") @Isolated @@ -317,6 +321,11 @@ public final class XMaterialUtil> { */ @NotNull public static final XMaterialUtil FENCES; + /** + * Tag representing all possible variants of filled cauldron + */ + @NotNull + public static final XMaterialUtil FILLED_CAULDRONS; /** * Tag representing all possible variants fire */ @@ -794,6 +803,11 @@ public final class XMaterialUtil> { */ @NotNull public static final XMaterialUtil VALID_SPAWN; + /** + * Tag representing all possible variants of wall hanging sign + */ + @NotNull + public static final XMaterialUtil WALL_HANGING_SIGNS; /** * Tag representing all possible block types that can override a wall post creation */ @@ -804,6 +818,11 @@ public final class XMaterialUtil> { */ @NotNull public static final XMaterialUtil WALL_SIGNS; + /** + * Tag representing all possible types of wall torches + */ + @NotNull + public static final XMaterialUtil WALL_TORCHES; /** * Tag representing all different types of walls */ @@ -1073,6 +1092,7 @@ public final class XMaterialUtil> { static { // wooded material STANDING_SIGNS = new XMaterialUtil<>(findAllWoodTypes("SIGN")); WALL_SIGNS = new XMaterialUtil<>(findAllWoodTypes("WALL_SIGN")); + WALL_HANGING_SIGNS = new XMaterialUtil<>(findAllWoodTypes("WALL_HANGING_SIGN")); HANGING_SIGNS = new XMaterialUtil<>(findAllWoodTypes("HANGING_SIGN")); WOODEN_PRESSURE_PLATES = new XMaterialUtil<>(findAllWoodTypes("PRESSURE_PLATE")); WOODEN_DOORS = new XMaterialUtil<>(findAllWoodTypes("DOOR")); @@ -1130,6 +1150,9 @@ public final class XMaterialUtil> { WALL_HEADS = new XMaterialUtil<>(XMaterial.class, new XMaterialUtil<>(findMaterialsEndingWith("WALL_HEAD")), new XMaterialUtil<>(XMaterial.WITHER_SKELETON_WALL_SKULL, XMaterial.SKELETON_WALL_SKULL)); + WALL_TORCHES = new XMaterialUtil<>(XMaterial.WALL_TORCH, + XMaterial.SOUL_WALL_TORCH, + XMaterial.REDSTONE_WALL_TORCH); WALLS = new XMaterialUtil<>(XMaterial.POLISHED_DEEPSLATE_WALL, XMaterial.NETHER_BRICK_WALL, XMaterial.POLISHED_BLACKSTONE_WALL, @@ -1200,6 +1223,9 @@ public final class XMaterialUtil> { XMaterial.PUMPKIN_STEM); CAMPFIRES = new XMaterialUtil<>(XMaterial.CAMPFIRE, XMaterial.SOUL_CAMPFIRE); + FILLED_CAULDRONS = new XMaterialUtil<>(XMaterial.LAVA_CAULDRON, + XMaterial.POWDER_SNOW_CAULDRON, + XMaterial.WATER_CAULDRON); CAULDRONS = new XMaterialUtil<>(XMaterial.CAULDRON, XMaterial.LAVA_CAULDRON, XMaterial.POWDER_SNOW_CAULDRON, @@ -1308,7 +1334,8 @@ public final class XMaterialUtil> { XMaterial.POTTED_JUNGLE_SAPLING, XMaterial.POTTED_BIRCH_SAPLING, XMaterial.POTTED_MANGROVE_PROPAGULE, - XMaterial.POTTED_CHERRY_SAPLING); + XMaterial.POTTED_CHERRY_SAPLING, + XMaterial.POTTED_TORCHFLOWER); FOX_FOOD = new XMaterialUtil<>(XMaterial.GLOW_BERRIES, XMaterial.SWEET_BERRIES); FOXES_SPAWNABLE_ON = new XMaterialUtil<>(XMaterial.SNOW, @@ -1554,7 +1581,7 @@ public final class XMaterialUtil> { XMaterial.LARGE_FERN, XMaterial.LILAC, XMaterial.ROSE_BUSH, - XMaterial.GRASS); + XMaterial.SHORT_GRASS); SMALL_DRIPLEAF_PLACEABLE = new XMaterialUtil<>(XMaterial.CLAY, XMaterial.MOSS_BLOCK); NON_FLAMMABLE_WOOD = new XMaterialUtil<>(XMaterial.CRIMSON_PLANKS, @@ -2216,7 +2243,7 @@ public final class XMaterialUtil> { XMaterial.SMALL_DRIPLEAF, XMaterial.LOOM, XMaterial.BEEHIVE, - XMaterial.GRASS, + XMaterial.SHORT_GRASS, XMaterial.HANGING_ROOTS, XMaterial.CHORUS_FLOWER, XMaterial.ATTACHED_PUMPKIN_STEM, @@ -2262,10 +2289,15 @@ public final class XMaterialUtil> { FIRE = new XMaterialUtil<>(XMaterial.FIRE, XMaterial.SOUL_FIRE); FLUID = new XMaterialUtil<>(XMaterial.LAVA, XMaterial.WATER); - INVENTORY_NOT_DISPLAYABLE = new XMaterialUtil<>(XMaterial.class, AIR, FIRE, FLUID, PORTALS, WALL_SIGNS, - CORAL_FANS, WALL_HEADS, CANDLE_CAKES, WALL_BANNERS, FLOWER_POTS, - new XMaterialUtil<>(XMaterial.SWEET_BERRY_BUSH, XMaterial.CHORUS_PLANT, XMaterial.KELP_PLANT, - XMaterial.CAVE_VINES_PLANT, XMaterial.TWISTING_VINES_PLANT, XMaterial.WEEPING_VINES_PLANT)); + INVENTORY_NOT_DISPLAYABLE = new XMaterialUtil<>(XMaterial.class, AIR, CAVE_VINES, FILLED_CAULDRONS, FIRE, FLUID, PORTALS, + WALL_SIGNS, WALL_HANGING_SIGNS, WALL_TORCHES, ALIVE_CORAL_WALL_FANS, DEAD_CORAL_WALL_FANS, WALL_HEADS, + CANDLE_CAKES, WALL_BANNERS, FLOWER_POTS.without(XMaterial.FLOWER_POT), CROPS.without(XMaterial.WHEAT), + new XMaterialUtil<>(XMaterial.BIG_DRIPLEAF_STEM, XMaterial.SWEET_BERRY_BUSH, XMaterial.KELP_PLANT, + XMaterial.FROSTED_ICE, XMaterial.ATTACHED_MELON_STEM, XMaterial.ATTACHED_PUMPKIN_STEM, + XMaterial.COCOA, XMaterial.MOVING_PISTON, XMaterial.PISTON_HEAD, XMaterial.PITCHER_CROP, + XMaterial.POWDER_SNOW, XMaterial.REDSTONE_WIRE, XMaterial.TALL_SEAGRASS, XMaterial.TRIPWIRE, + XMaterial.TORCHFLOWER_CROP, XMaterial.BUBBLE_COLUMN, XMaterial.TWISTING_VINES_PLANT, + XMaterial.WEEPING_VINES_PLANT, XMaterial.BAMBOO_SAPLING)); } @NotNull @@ -2276,12 +2308,156 @@ private XMaterialUtil(@NotNull T... values) { this.values = Collections.unmodifiableSet(EnumSet.copyOf(Arrays.asList(values))); } + /** + * Compiles a list of string checkers for various classes like {@link XMaterial}, {@link XSound}, etc. + * Mostly used for configs. + *

+ * Supports {@link String#contains} {@code CONTAINS:NAME} and Regular Expression {@code REGEX:PATTERN} formats. + *

+ * Example: + *

+     *     XMaterial material = {@link XMaterial#matchXMaterial(ItemStack)};
+     *     if (XMaterialUtil.anyMatch(XMaterialUtil.stringMatcher(plugin.getConfig().getStringList("disabled-items"), null)) return;
+     * 
+ *
+ * {@code CONTAINS} Examples: + *
+     *     {@code "CONTAINS:CHEST" -> CHEST, ENDERCHEST, TRAPPED_CHEST -> true}
+     *     {@code "cOnTaINS:dYe" -> GREEN_DYE, YELLOW_DYE, BLUE_DYE, INK_SACK -> true}
+     * 
+ *

+ * {@code REGEX} Examples + *

+     *     {@code "REGEX:^.+_.+_.+$" -> Every Material with 3 underlines or more: SHULKER_SPAWN_EGG, SILVERFISH_SPAWN_EGG, SKELETON_HORSE_SPAWN_EGG}
+     *     {@code "REGEX:^.{1,3}$" -> Material names that have 3 letters only: BED, MAP, AIR}
+     * 
+ *

+ * The reason that there are tags for {@code CONTAINS} and {@code REGEX} is for the performance. + * Server owners should be advised to avoid using {@code REGEX} tag if they can use the {@code CONTAINS} tag instead. + *

+ * Want to learn RegEx? You can mess around in RegExr website. + * + * @param elements the material names to check base material on. + * @return a compiled list of enum matchers. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static List> stringMatcher(@Nullable Collection elements, + @Nullable Collection errors) { + if (elements == null || elements.isEmpty()) return new ArrayList<>(); + List> matchers = new ArrayList<>(elements.size()); + + for (String comp : elements) { + String checker = comp.toUpperCase(Locale.ENGLISH); + if (checker.startsWith("CONTAINS:")) { + comp = XMaterial.format(checker.substring(9)); + matchers.add(new Matcher.TextMatcher<>(comp, true)); + continue; + } + if (checker.startsWith("REGEX:")) { + comp = comp.substring(6); + try { + matchers.add(new Matcher.RegexMatcher<>(Pattern.compile(comp))); + } catch (Throwable e) { + if (errors != null) errors.add(new Matcher.Error(comp, "REGEX", e)); + } + continue; + } + if (checker.startsWith("TAG:")) { + comp = XMaterial.format(comp.substring(4)); + try { + Field field = XMaterialUtil.class.getField(comp); + XMaterialUtil obj = (XMaterialUtil) field.get(null); + matchers.add(new Matcher.XMaterialUtilMatcher(obj)); + } catch (Throwable e) { + if (errors != null) errors.add(new Matcher.Error(comp, "TAG", e)); + } + } + + matchers.add(new Matcher.TextMatcher<>(comp, false)); + } + + return matchers; + } + + public static boolean anyMatch(T target, Collection> matchers) { + return matchers.stream().anyMatch(x -> x.matches(target)); + } + + @Isolated + public abstract static class Matcher { + + @Isolated + public static final class Error extends RuntimeException { + public final String matcher; + + public Error(String matcher, String message, Throwable cause) { + super(message, cause); + this.matcher = matcher; + } + } + + public abstract boolean matches(T object); + + @Isolated + public static final class TextMatcher extends Matcher { + + public final String text; + public final boolean contains; + + public TextMatcher(String text, boolean contains) { + this.text = text; + this.contains = contains; + } + + @Override + public boolean matches(T object) { + String name = object instanceof Enum ? ((Enum) object).name() : object.toString(); + return contains ? name.contains(this.text) : name.equals(this.text); + } + } + + @Isolated + public static final class RegexMatcher extends Matcher { + + public final Pattern regex; + + public RegexMatcher(Pattern regex) { + this.regex = regex; + } + + @Override + public boolean matches(T object) { + String name = object instanceof Enum ? ((Enum) object).name() : object.toString(); + return regex.matcher(name).matches(); + } + } + + @Isolated + public static final class XMaterialUtilMatcher> extends Matcher { + + public final XMaterialUtil matcher; + + public XMaterialUtilMatcher(XMaterialUtil matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(T object) { + return matcher.isTagged(object); + } + } + } + @SafeVarargs private XMaterialUtil(@NotNull Class clazz, @NotNull XMaterialUtil... values) { this.values = EnumSet.noneOf(clazz); this.inheritFrom(values); } + private XMaterialUtil(@NotNull Set values) { + this.values = Collections.unmodifiableSet(values); + } + private static XMaterial[] findAllColors(String material) { String[] colorPrefixes = {"ORANGE", "LIGHT_BLUE", "GRAY", "BLACK", "MAGENTA", "PINK", "BLUE", "GREEN", "CYAN", "PURPLE", "YELLOW", "LIME", "LIGHT_GRAY", "WHITE", "BROWN", "RED"}; @@ -2607,6 +2783,14 @@ public boolean isTagged(@Nullable T value) { return value != null && this.values.contains(value); } + @SafeVarargs + private final XMaterialUtil without(T... without) { + Set ignore = new HashSet<>(); + Collections.addAll(ignore, without); + Set newValues = values.stream().filter(t -> !ignore.contains(t)).collect(Collectors.toSet()); + return new XMaterialUtil<>(newValues); + } + @SafeVarargs private final XMaterialUtil inheritFrom(@NotNull XMaterialUtil... values) { // Copied because of Collections.unmodifiableSet.