From 3f1f8379cf9826c3c7d7340a1ed9242d7dce287f Mon Sep 17 00:00:00 2001 From: Anthony Hilyard Date: Tue, 23 Nov 2021 15:14:39 -0800 Subject: Added NBT tag selector. Added selector syntax validator. --- .../com/anthonyhilyard/iceberg/util/Selectors.java | 180 +++++++++++++++++++++ 1 file changed, 180 insertions(+) (limited to 'src/main/java/com/anthonyhilyard/iceberg/util') diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java index 65a2ebd..6f450f1 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java +++ b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java @@ -4,9 +4,14 @@ import net.minecraft.world.item.ItemStack; import java.util.HashMap; import java.util.Map; +import java.util.function.BiPredicate; + import java.util.List; import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextColor; import net.minecraft.resources.ResourceLocation; @@ -23,13 +28,109 @@ public class Selectors put("epic", Rarity.EPIC); }}; + private static Map> nbtComparators = new HashMap>() {{ + put("=", (tag, value) -> tag.getAsString().contentEquals(value)); + + put("!=", (tag, value) -> !tag.getAsString().contentEquals(value)); + + put(">", (tag, value) -> { + try + { + double parsedValue = Double.valueOf(value); + if (tag instanceof NumericTag) + { + return ((NumericTag)tag).getAsDouble() > parsedValue; + } + else + { + return false; + } + } + catch (Exception e) + { + return false; + } + }); + + put("<", (tag, value) -> { + try + { + double parsedValue = Double.valueOf(value); + if (tag instanceof NumericTag) + { + return ((NumericTag)tag).getAsDouble() < parsedValue; + } + else + { + return false; + } + } + catch (Exception e) + { + return false; + } + }); + }}; + + /** + * Returns true if this selector is syntactically valid. + * @param value The selector. + * @return True if the selector syntax is valid, false otherwise. + */ + public static boolean validateSelector(String value) + { + // This is a tag, which should be a resource location. + if (value.startsWith("$")) + { + return ResourceLocation.isValidResourceLocation(value.substring(1)); + } + // Mod IDs need to conform to this regex: ^[a-z][a-z0-9_-]{1,63}$ + else if (value.startsWith("@")) + { + return value.substring(1).matches("^[a-z][a-z0-9_-]{1,63}$"); + } + // If this is a rarity, make sure it's a valid one. + else if (value.startsWith("!")) + { + return rarities.keySet().contains(value.substring(1).toLowerCase()); + } + // If this is a hex color, ensure it's in a valid format. + else if (value.startsWith("#")) + { + return TextColor.parseColor(value) != null; + } + // Text matches are always considered valid. + else if (value.startsWith("%") || value.startsWith("^")) + { + return true; + } + // Any text is valid for NBT tag selectors. + else if (value.startsWith("&")) + { + return true; + } + // Otherwise it's an item, so just make sure it's a value resource location. + else + { + return value == null || value == "" || ResourceLocation.isValidResourceLocation(value); + } + } + + /** + * Returns true if the given item is matched by the given selector. + * @param item An ItemStack instance of an item to check. + * @param selector A selector string to check against. + * @return True if the item matches, false otherwise. + */ public static boolean itemMatches(ItemStack item, String selector) { String itemResourceLocation = item.getItem().getRegistryName().toString(); + // Item ID if (selector.equals(itemResourceLocation) || selector.equals(itemResourceLocation.replace("minecraft:", ""))) { return true; } + // Item name color else if (selector.startsWith("#")) { TextColor entryColor = TextColor.parseColor(selector); @@ -38,6 +139,7 @@ public class Selectors return true; } } + // Vanilla rarity else if (selector.startsWith("!")) { if (item.getRarity() == rarities.get(selector.substring(1))) @@ -45,6 +147,7 @@ public class Selectors return true; } } + // Mod ID else if (selector.startsWith("@")) { if (itemResourceLocation.startsWith(selector.substring(1) + ":")) @@ -52,6 +155,7 @@ public class Selectors return true; } } + // Item tag else if (selector.startsWith("$")) { if (ItemTags.getAllTags().getTagOrEmpty(new ResourceLocation(selector.substring(1))).getValues().contains(item.getItem())) @@ -59,6 +163,7 @@ public class Selectors return true; } } + // Item display name else if (selector.startsWith("%")) { if (item.getDisplayName().getString().contains(selector.substring(1))) @@ -66,6 +171,7 @@ public class Selectors return true; } } + // Tooltip text else if (selector.startsWith("^")) { Minecraft mc = Minecraft.getInstance(); @@ -82,7 +188,81 @@ public class Selectors return true; } } + // NBT tag + else if (selector.startsWith("&")) + { + String tagName = selector.substring(1); + String tagValue = null; + BiPredicate valueChecker = null; + + // This implementation means tag names containing and comparator strings can't be compared. + // Hopefully this isn't common. + for (String comparator : nbtComparators.keySet()) + { + if (tagName.contains(comparator)) + { + valueChecker = nbtComparators.get(comparator); + String[] components = tagName.split(comparator); + tagName = components[0]; + if (components.length > 1) + { + tagValue = components[1]; + } + break; + } + } + + // Look for a tag matching the given name. + Tag matchedTag = getSubtag(item.getTag(), tagName); + if (matchedTag != null) + { + // A tag value of null means that we are just looking for the presence of this tag. + if (tagValue == null) + { + return true; + } + // Otherwise, we will use the provided comparator. + else + { + if (valueChecker != null) + { + return valueChecker.test(matchedTag, tagValue); + } + } + } + } return false; } + + /** + * Retrieves the first inner tag with the given key, or null if there is no match. + */ + private static Tag getSubtag(CompoundTag tag, String key) + { + if (tag == null) + { + return null; + } + + if (tag.contains(key)) + { + return tag.get(key); + } + else + { + for (String innerKey : tag.getAllKeys()) + { + if (tag.getTagType(innerKey) == Tag.TAG_COMPOUND) + { + Tag innerTag = getSubtag(tag.getCompound(innerKey), key); + if (innerTag != null) + { + return innerTag; + } + } + } + return null; + } + } } \ No newline at end of file -- cgit