diff --git a/src/main/java/ch/njol/skript/aliases/Aliases.java b/src/main/java/ch/njol/skript/aliases/Aliases.java index fe1a3a624c4..111ffc5adcd 100644 --- a/src/main/java/ch/njol/skript/aliases/Aliases.java +++ b/src/main/java/ch/njol/skript/aliases/Aliases.java @@ -268,7 +268,7 @@ public static ItemType parseItemType(String s) { } String lc = s.toLowerCase(Locale.ENGLISH); - String of = Language.getSpaced("enchantments.of").toLowerCase(); + String of = Language.getSpaced("of").toLowerCase(); int c = -1; outer: while ((c = lc.indexOf(of, c + 1)) != -1) { ItemType t2 = t.clone(); diff --git a/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java b/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java index fcebff78917..7247bba5d37 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java @@ -18,26 +18,165 @@ */ package ch.njol.skript.bukkitutil; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.localization.Language; +import ch.njol.util.StringUtils; +import ch.njol.yggdrasil.Fields; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.enchantments.Enchantment; -import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.Nullable; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import ch.njol.skript.Skript; +import java.io.StreamCorruptedException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; /** * Maps enchantments to their keys. */ public class EnchantmentUtils { + private static final Map NAMES = new HashMap<>(); + private static final Map PATTERNS = new HashMap<>(); + private static final boolean HAS_REGISTRY = BukkitUtils.registryExists("ENCHANTMENT"); + + static { + if (!HAS_REGISTRY) { + Language.addListener(() -> { + NAMES.clear(); + PATTERNS.clear(); + for (Enchantment enchantment : Enchantment.values()) { + NamespacedKey key = enchantment.getKey(); + final String[] names = Language.getList("enchantments." + key.getKey()); + + if (!names[0].startsWith("enchantments.")) { + NAMES.put(enchantment, names[0]); + // Add lang file names + for (String name : names) + PATTERNS.put(name.toLowerCase(Locale.ENGLISH), enchantment); + } + // If Minecraft provided, add key without namespace and underscores (ex: "fire aspect") + if (key.getNamespace().equalsIgnoreCase(NamespacedKey.MINECRAFT)) + PATTERNS.put(key.getKey().replace("_", " "), enchantment); + // Add full namespaced key as pattern (ex: "minecraft:fire_aspect", "custom:floopy_floopy") + PATTERNS.put(key.toString(), enchantment); + } + }); + } + } + public static String getKey(Enchantment enchantment) { - return enchantment.getKey().getKey(); + return enchantment.getKey().toString(); } @Nullable public static Enchantment getByKey(String key) { - return Enchantment.getByKey(NamespacedKey.minecraft(key)); + if (!key.contains(":")) { + // Old method for old variables + return Enchantment.getByKey(NamespacedKey.minecraft(key)); + } else { + NamespacedKey namespacedKey = NamespacedKey.fromString(key); + if (namespacedKey == null) + return null; + + if (HAS_REGISTRY) { + return Registry.ENCHANTMENT.get(namespacedKey); + } else { + return Enchantment.getByKey(namespacedKey); + } + } + } + + @Nullable + public static Enchantment parseEnchantment(String s) { + return PATTERNS.get(s); + } + + @SuppressWarnings("null") + public static Collection getNames() { + return NAMES.values(); + } + + @SuppressWarnings("null") + public static String toString(final Enchantment enchantment) { + // If we have a name in the lang file, return that first + if (NAMES.containsKey(enchantment)) + return NAMES.get(enchantment); + + // If no name is available, return the namespaced key + return enchantment.getKey().toString(); + } + + // REMIND flags? + @SuppressWarnings("null") + public static String toString(final Enchantment enchantment, final int flags) { + return toString(enchantment); + } + + public static ClassInfo createClassInfo() { + return new ClassInfo<>(Enchantment.class, "enchantment") + .parser(new Parser<>() { + @Override + @Nullable + public Enchantment parse(final String s, final ParseContext context) { + return EnchantmentUtils.parseEnchantment(s); + } + + @Override + public String toString(final Enchantment e, final int flags) { + return EnchantmentUtils.toString(e, flags); + } + + @Override + public String toVariableNameString(final Enchantment e) { + return "" + EnchantmentUtils.getKey(e); + } + }).serializer(new Serializer<>() { + @Override + public Fields serialize(final Enchantment ench) { + final Fields f = new Fields(); + f.putObject("key", EnchantmentUtils.getKey(ench)); + return f; + } + + @Override + public boolean canBeInstantiated() { + return false; + } + + @Override + public void deserialize(final Enchantment o, final Fields f) { + assert false; + } + + @Override + protected Enchantment deserialize(final Fields fields) throws StreamCorruptedException { + final String key = fields.getObject("key", String.class); + assert key != null; // If a key happens to be null, something went really wrong... + final Enchantment e = EnchantmentUtils.getByKey(key); + if (e == null) + throw new StreamCorruptedException("Invalid enchantment " + key); + return e; + } + + @Override + @Nullable + public Enchantment deserialize(String s) { + return Enchantment.getByName(s); + } + + @Override + public boolean mustSyncDeserialization() { + return false; + } + }) + .usage(StringUtils.join(EnchantmentUtils.getNames(), ", ")) + .supplier(Enchantment.values()); } } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index b48c69149d7..231d5ef9986 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -100,7 +100,6 @@ import ch.njol.skript.localization.Language; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.BlockUtils; -import ch.njol.skript.util.EnchantmentType; import ch.njol.skript.util.PotionEffectUtils; import ch.njol.skript.util.StringMode; import ch.njol.util.StringUtils; @@ -1078,12 +1077,12 @@ protected boolean canBeInstantiated() { public PotionEffectType parse(final String s, final ParseContext context) { return PotionEffectUtils.parseType(s); } - + @Override public String toString(final PotionEffectType p, final int flags) { return PotionEffectUtils.toString(p, flags); } - + @Override public String toVariableNameString(final PotionEffectType p) { return "" + p.getName(); @@ -1223,74 +1222,23 @@ public boolean mustSyncDeserialization() { return true; } })); - - Classes.registerClass(new ClassInfo<>(Enchantment.class, "enchantment") + + ClassInfo enchantmentClassInfo; + if (BukkitUtils.registryExists("ENCHANTMENT")) { + enchantmentClassInfo = new RegistryClassInfo<>(Enchantment.class, Registry.ENCHANTMENT, "enchantment", "enchantments"); + } else { + enchantmentClassInfo = EnchantmentUtils.createClassInfo(); + } + Classes.registerClass(enchantmentClassInfo .user("enchantments?") .name("Enchantment") .description("An enchantment, e.g. 'sharpness' or 'fortune'. Unlike enchantment type " + - "this type has no level, but you usually don't need to use this type anyway.") - .usage(StringUtils.join(EnchantmentType.getNames(), ", ")) + "this type has no level, but you usually don't need to use this type anyway.", + "NOTE: Minecraft namespaces are supported, ex: 'minecraft:basalt_deltas'.", + "As of Minecraft 1.21 this will also support custom enchantments using namespaces, ex: 'myenchants:explosive'.") .examples("") .since("1.4.6") - .before("enchantmenttype") - .supplier(Enchantment.values()) - .parser(new Parser() { - @Override - @Nullable - public Enchantment parse(final String s, final ParseContext context) { - return EnchantmentType.parseEnchantment(s); - } - - @Override - public String toString(final Enchantment e, final int flags) { - return EnchantmentType.toString(e, flags); - } - - @Override - public String toVariableNameString(final Enchantment e) { - return "" + EnchantmentUtils.getKey(e); - } - }) - .serializer(new Serializer() { - @Override - public Fields serialize(final Enchantment ench) { - final Fields f = new Fields(); - f.putObject("key", EnchantmentUtils.getKey(ench)); - return f; - } - - @Override - public boolean canBeInstantiated() { - return false; - } - - @Override - public void deserialize(final Enchantment o, final Fields f) { - assert false; - } - - @Override - protected Enchantment deserialize(final Fields fields) throws StreamCorruptedException { - final String key = fields.getObject("key", String.class); - assert key != null; // If a key happens to be null, something went really wrong... - final Enchantment e = EnchantmentUtils.getByKey(key); - if (e == null) - throw new StreamCorruptedException("Invalid enchantment " + key); - return e; - } - - // return "" + e.getId(); - @Override - @Nullable - public Enchantment deserialize(String s) { - return Enchantment.getByName(s); - } - - @Override - public boolean mustSyncDeserialization() { - return false; - } - })); + .before("enchantmenttype")); Material[] allMaterials = Material.values(); Classes.registerClass(new ClassInfo<>(Material.class, "material") @@ -1516,12 +1464,12 @@ public boolean canParse(ParseContext context) { @Override public String toString(EnchantmentOffer eo, int flags) { - return EnchantmentType.toString(eo.getEnchantment(), flags) + " " + eo.getEnchantmentLevel(); + return EnchantmentUtils.toString(eo.getEnchantment(), flags) + " " + eo.getEnchantmentLevel(); } @Override public String toVariableNameString(EnchantmentOffer eo) { - return "offer:" + EnchantmentType.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel(); + return "offer:" + EnchantmentUtils.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel(); } })); diff --git a/src/main/java/ch/njol/skript/util/EnchantmentType.java b/src/main/java/ch/njol/skript/util/EnchantmentType.java index 1ef13893fc9..97f4b8d22d0 100644 --- a/src/main/java/ch/njol/skript/util/EnchantmentType.java +++ b/src/main/java/ch/njol/skript/util/EnchantmentType.java @@ -18,30 +18,27 @@ */ package ch.njol.skript.util; -import java.util.Collection; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Pattern; - -import org.bukkit.enchantments.Enchantment; -import org.eclipse.jdt.annotation.Nullable; - import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.EnchantmentUtils; -import ch.njol.skript.localization.Language; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.registrations.Classes; import ch.njol.yggdrasil.YggdrasilSerializable; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Pattern; /** * @author Peter Güttinger */ public class EnchantmentType implements YggdrasilSerializable { - - private final static String LANGUAGE_NODE = "enchantments"; - + + private static @Nullable Parser ENCHANTMENT_PARSER = null; private final Enchantment type; private final int level; - + /** * Used for deserialisation only */ @@ -50,39 +47,41 @@ private EnchantmentType() { type = null; level = -1; } - + public EnchantmentType(final Enchantment type) { assert type != null; this.type = type; this.level = -1; } + public EnchantmentType(final Enchantment type, final int level) { assert type != null; this.type = type; this.level = level; } - + /** * @return level or 1 if level == -1 */ public int getLevel() { return level == -1 ? 1 : level; } - + /** * @return the internal level, can be -1 */ public int getInternalLevel() { return level; } - + @Nullable public Enchantment getType() { return type; } - + /** * Checks whether the given item type has this enchantment. + * * @param item the item to be checked. * @deprecated Use {@link ItemType#hasEnchantments(Enchantment...)} */ @@ -90,77 +89,50 @@ public Enchantment getType() { public boolean has(final ItemType item) { return item.hasEnchantments(type); } - + @Override public String toString() { - return toString(type) + (level == -1 ? "" : " " + level); - } - - @SuppressWarnings("null") - public static String toString(final Enchantment e) { - return NAMES.get(e); - } - - // REMIND flags? - @SuppressWarnings("null") - public static String toString(final Enchantment e, final int flags) { - return NAMES.get(e); - } - - private final static Map NAMES = new HashMap<>(); - private final static Map PATTERNS = new HashMap<>(); - - static { - Language.addListener(() -> { - NAMES.clear(); - for (Enchantment e : Enchantment.values()) { - assert e != null; - final String[] names = Language.getList(LANGUAGE_NODE + ".names." + EnchantmentUtils.getKey(e)); - NAMES.put(e, names[0]); - - for (String name : names) - PATTERNS.put(name.toLowerCase(Locale.ENGLISH), e); - } - }); + return EnchantmentUtils.toString(type) + (level == -1 ? "" : " " + level); } - + @SuppressWarnings("null") private final static Pattern pattern = Pattern.compile(".+ \\d+"); - + /** * Parses an enchantment type from string. This includes an {@link Enchantment} * and its level. + * * @param s String to parse. * @return Enchantment type, or null if parsing failed. */ @Nullable public static EnchantmentType parse(final String s) { + if (ENCHANTMENT_PARSER == null) { + ClassInfo classInfo = Classes.getExactClassInfo(Enchantment.class); + if (classInfo == null) { + throw new IllegalStateException("Enchantment ClassInfo not found"); + } + ENCHANTMENT_PARSER = (Parser) classInfo.getParser(); + if (ENCHANTMENT_PARSER == null) { + throw new IllegalStateException("Enchantment parser not found"); + } + } if (pattern.matcher(s).matches()) { String name = s.substring(0, s.lastIndexOf(' ')); assert name != null; - final Enchantment ench = parseEnchantment(name); + final Enchantment ench = ENCHANTMENT_PARSER.parse(name, ParseContext.DEFAULT); if (ench == null) return null; String level = s.substring(s.lastIndexOf(' ') + 1); assert level != null; return new EnchantmentType(ench, Utils.parseInt(level)); } - final Enchantment ench = parseEnchantment(s); + final Enchantment ench = ENCHANTMENT_PARSER.parse(s, ParseContext.DEFAULT); if (ench == null) return null; return new EnchantmentType(ench, -1); } - - @Nullable - public static Enchantment parseEnchantment(final String s) { - return PATTERNS.get(s.toLowerCase(Locale.ENGLISH)); - } - - @SuppressWarnings("null") - public static Collection getNames() { - return NAMES.values(); - } - + @Override public int hashCode() { final int prime = 31; @@ -169,7 +141,7 @@ public int hashCode() { result = prime * result + type.hashCode(); return result; } - + @Override public boolean equals(final @Nullable Object obj) { if (this == obj) @@ -183,5 +155,5 @@ public boolean equals(final @Nullable Object obj) { return false; return type.equals(other.type); } - + } diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 38d8cc68f1f..7ba10ec99f7 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -13,6 +13,7 @@ not: not # not a: not a(n) # @a:not a @an:not an @x:not neither: neither nor: nor +of: of genders: 0: @@ -47,58 +48,56 @@ aliases: # -- Enchantments -- enchantments: - of: of - names: - protection: Protection - fire_protection: Fire Protection - feather_falling: Feather Falling - blast_protection: Blast Protection - projectile_protection: Projectile Protection - respiration: Respiration - aqua_affinity: Aqua Affinity - sharpness: Sharpness - smite: Smite - bane_of_arthropods: Bane of Arthropods - knockback: Knockback - fire_aspect: Fire Aspect - looting: Looting - efficiency: Efficiency - silk_touch: Silk Touch - unbreaking: Unbreaking - fortune: Fortune - power: Power - punch: Punch - flame: Flame - infinity: Infinity - thorns: Thorns - luck: Luck of the Sea - lure: Lure - depth_strider: Depth Strider - mending: Mending - frost_walker: Frost Walker - vanishing_curse: Curse of Vanishing - binding_curse: Curse of Binding - # It's sweeping on 1.10 and below for some reason. - sweeping: Sweeping Edge - sweeping_edge: Sweeping Edge - # New 1.13 Enchantments - channeling: Channeling, Channelling - riptide: Riptide - impaling: Impaling - loyalty: Loyalty - luck_of_the_sea: Luck of The Sea - # new 1.14 Enchantments - multishot: Multishot, Multi-Shot - piercing: Piercing - quick_charge: Quick Charge - # new 1.16 Enchantment - soul_speed: Soul Speed - # new 1.19 Enchantment - swift_sneak: Swift Sneak - # new 1.21 Enchantments (added in 1.20.5 experimental) - density: Density - breach: Breach - wind_burst: Wind Burst + protection: Protection + fire_protection: Fire Protection + feather_falling: Feather Falling + blast_protection: Blast Protection + projectile_protection: Projectile Protection + respiration: Respiration + aqua_affinity: Aqua Affinity + sharpness: Sharpness + smite: Smite + bane_of_arthropods: Bane of Arthropods + knockback: Knockback + fire_aspect: Fire Aspect + looting: Looting + efficiency: Efficiency + silk_touch: Silk Touch + unbreaking: Unbreaking + fortune: Fortune + power: Power + punch: Punch + flame: Flame + infinity: Infinity + thorns: Thorns + luck: Luck of the Sea + lure: Lure + depth_strider: Depth Strider + mending: Mending + frost_walker: Frost Walker + vanishing_curse: Curse of Vanishing + binding_curse: Curse of Binding + # It's sweeping on 1.10 and below for some reason. + sweeping: Sweeping Edge + sweeping_edge: Sweeping Edge + # New 1.13 Enchantments + channeling: Channeling, Channelling + riptide: Riptide + impaling: Impaling + loyalty: Loyalty + luck_of_the_sea: Luck of The Sea + # new 1.14 Enchantments + multishot: Multishot, Multi-Shot + piercing: Piercing + quick_charge: Quick Charge + # new 1.16 Enchantment + soul_speed: Soul Speed + # new 1.19 Enchantment + swift_sneak: Swift Sneak + # new 1.21 Enchantments (added in 1.20.5 experimental) + density: Density + breach: Breach + wind_burst: Wind Burst # -- Potion Effects -- potions: diff --git a/src/test/skript/tests/regressions/pull-6687-enchantment-update.sk b/src/test/skript/tests/regressions/pull-6687-enchantment-update.sk new file mode 100644 index 00000000000..94f4d9a3c42 --- /dev/null +++ b/src/test/skript/tests/regressions/pull-6687-enchantment-update.sk @@ -0,0 +1,11 @@ +test "Enchantment Registry Update": + set {_i} to diamond sword of unbreaking 3 and sharpness 10 + + assert enchantment level of unbreaking of {_i} = 3 with "Unbreaking enchant on {_i} should have been 3" + assert enchantment level of sharpness of {_i} = 10 with "Sharpness enchant on {_i} should have been 10" + + set enchantment level of lure of {_i} to 5 + assert enchantment level of lure of {_i} = 5 with "Lure enchant on {_i} should have been 5" + + set enchantment level of sharpness of {_i} to 5 + assert enchantment level of sharpness of {_i} = 5 with "Sharpness enchant on {_i} should have been 5"