From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Tue, 17 Jul 2018 01:27:15 -0400 Subject: [PATCH] Add Material Tags This adds a bunch of useful and missing Tags to be able to identify items that are related to each other by a trait. Co-authored-by: Jake Potrebic Co-authored-by: Lena Kolb Co-authored-by: Layla Silbernberg diff --git a/src/main/java/com/destroystokyo/paper/MaterialSetTag.java b/src/main/java/com/destroystokyo/paper/MaterialSetTag.java new file mode 100644 index 0000000000000000000000000000000000000000..a02a02aa0c87e0f0ed9e509e4dcab01565b3d92a --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/MaterialSetTag.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License + */ + +package com.destroystokyo.paper; + +import com.google.common.collect.Lists; +import io.papermc.paper.tag.BaseTag; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class MaterialSetTag extends BaseTag { + + /** + * @deprecated Use NamespacedKey version of constructor + */ + @Deprecated + public MaterialSetTag(@NotNull Predicate filter) { + this(null, Stream.of(Material.values()).filter(filter).collect(Collectors.toList())); + } + + /** + * @deprecated Use NamespacedKey version of constructor + */ + @Deprecated + public MaterialSetTag(@NotNull Collection materials) { + this(null, materials); + } + + /** + * @deprecated Use NamespacedKey version of constructor + */ + @Deprecated + public MaterialSetTag(@NotNull Material... materials) { + this(null, materials); + } + + public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Predicate filter) { + this(key, Stream.of(Material.values()).filter(filter).collect(Collectors.toList())); + } + + public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Material... materials) { + this(key, Lists.newArrayList(materials)); + } + + public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection materials) { + this(key != null ? key : NamespacedKey.randomKey(), materials, ((Predicate) Material::isLegacy).negate()); + } + + public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection materials, @NotNull Predicate...globalPredicates) { + super(Material.class, key != null ? key : NamespacedKey.randomKey(), materials, globalPredicates); + } + + @NotNull + @Override + protected Set getAllPossibleValues() { + return Stream.of(Material.values()).collect(Collectors.toSet()); + } + + @Override + @NotNull + protected String getName(@NotNull Material value) { + return value.name(); + } + + public boolean isTagged(@NotNull BlockData block) { + return isTagged(block.getMaterial()); + } + + public boolean isTagged(@NotNull BlockState block) { + return isTagged(block.getType()); + } + + public boolean isTagged(@NotNull Block block) { + return isTagged(block.getType()); + } + + public boolean isTagged(@NotNull ItemStack item) { + return isTagged(item.getType()); + } + + public boolean isTagged(@NotNull Material material) { + return this.tagged.contains(material); + } +} diff --git a/src/main/java/com/destroystokyo/paper/MaterialTags.java b/src/main/java/com/destroystokyo/paper/MaterialTags.java new file mode 100644 index 0000000000000000000000000000000000000000..be212b4fbeabab32a4dab6ae554768c368efaa88 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/MaterialTags.java @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.destroystokyo.paper; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; + +/** + * Represents a collection tags to identify materials that share common properties. + * Will map to minecraft for missing tags, as well as custom ones that may be useful. + *

+ * All tags in this class are unmodifiable, attempting to modify them will throw an + * {@link UnsupportedOperationException}. + */ +@SuppressWarnings({"NonFinalUtilityClass", "unused", "WeakerAccess"}) +public class MaterialTags { + + private static NamespacedKey keyFor(String key) { + //noinspection deprecation + return new NamespacedKey("paper", key + "_settag"); + } + public static final MaterialSetTag ARROWS = new MaterialSetTag(keyFor("arrows")) + .endsWith("ARROW") + .ensureSize("ARROWS", 3).lock(); + + /** + * Covers all colors of beds. + */ + public static final MaterialSetTag BEDS = new MaterialSetTag(keyFor("beds")) + .endsWith("_BED") + .ensureSize("BEDS", 16).lock(); + + /** + * Covers all bucket items. + */ + public static final MaterialSetTag BUCKETS = new MaterialSetTag(keyFor("buckets")) + .endsWith("BUCKET") + .ensureSize("BUCKETS", 11).lock(); + + /** + * Covers coal and charcoal. + */ + public static final MaterialSetTag COALS = new MaterialSetTag(keyFor("coals")) + .add(Material.COAL, Material.CHARCOAL).lock(); + + /** + * Covers both cobblestone wall variants. + */ + public static final MaterialSetTag COBBLESTONE_WALLS = new MaterialSetTag(keyFor("cobblestone_walls")) + .endsWith("COBBLESTONE_WALL") + .ensureSize("COBBLESTONE_WALLS", 2).lock(); + + /** + * Covers both cobblestone and mossy Cobblestone. + */ + public static final MaterialSetTag COBBLESTONES = new MaterialSetTag(keyFor("cobblestones")) + .add(Material.COBBLESTONE, Material.MOSSY_COBBLESTONE).lock(); + + /** + * Covers all colors of concrete. + */ + public static final MaterialSetTag CONCRETES = new MaterialSetTag(keyFor("concretes")) + .endsWith("_CONCRETE") + .ensureSize("CONCRETES", 16).lock(); + + /** + * Covers all colors of concrete powder. + */ + public static final MaterialSetTag CONCRETE_POWDER = new MaterialSetTag(keyFor("concrete_powder")) + .endsWith("_CONCRETE_POWDER") + .ensureSize("CONCRETE_POWDER", 16).lock(); + + /** + * Covers the two types of cooked fish. + */ + public static final MaterialSetTag COOKED_FISH = new MaterialSetTag(keyFor("cooked_fish")) + .add(Material.COOKED_COD, Material.COOKED_SALMON).lock(); + + /** + * Covers all variants of doors. + */ + public static final MaterialSetTag DOORS = new MaterialSetTag(keyFor("doors")) + .endsWith("_DOOR") + .ensureSize("DOORS", 20).lock(); + + /** + * Covers all dyes. + */ + public static final MaterialSetTag DYES = new MaterialSetTag(keyFor("dyes")) + .endsWith("_DYE") + .ensureSize("DYES", 16).lock(); + + /** + * Covers all variants of gates. + */ + public static final MaterialSetTag FENCE_GATES = new MaterialSetTag(keyFor("fence_gates")) + .endsWith("_GATE") + .ensureSize("FENCE_GATES", 11).lock(); + + /** + * Covers all variants of fences. + */ + public static final MaterialSetTag FENCES = new MaterialSetTag(keyFor("fences")) + .endsWith("_FENCE") + .ensureSize("FENCES", 12).lock(); + + /** + * Covers all variants of fish buckets. + */ + public static final MaterialSetTag FISH_BUCKETS = new MaterialSetTag(keyFor("fish_buckets")) + .add(Material.COD_BUCKET, Material.PUFFERFISH_BUCKET, Material.SALMON_BUCKET, Material.TROPICAL_FISH_BUCKET).lock(); + + /** + * Covers the non-colored glass and 16 stained glass (not panes). + */ + public static final MaterialSetTag GLASS = new MaterialSetTag(keyFor("glass")) + .endsWith("_GLASS") + .add(Material.GLASS) + .ensureSize("GLASS", 18).lock(); + + /** + * Covers the non-colored glass panes and stained glass panes (panes only). + */ + public static final MaterialSetTag GLASS_PANES = new MaterialSetTag(keyFor("glass_panes")) + .endsWith("GLASS_PANE") + .ensureSize("GLASS_PANES", 17).lock(); + + /** + * Covers all glazed terracotta blocks. + */ + public static final MaterialSetTag GLAZED_TERRACOTTA = new MaterialSetTag(keyFor("glazed_terracotta")) + .endsWith("GLAZED_TERRACOTTA") + .ensureSize("GLAZED_TERRACOTTA", 16).lock(); + + /** + * Covers the colors of stained terracotta. + */ + public static final MaterialSetTag STAINED_TERRACOTTA = new MaterialSetTag(keyFor("stained_terracotta")) + .endsWith("TERRACOTTA") + .not(Material.TERRACOTTA) + .notEndsWith("GLAZED_TERRACOTTA") + .ensureSize("STAINED_TERRACOTTA", 16).lock(); + + /** + * Covers terracotta along with the stained variants. + */ + public static final MaterialSetTag TERRACOTTA = new MaterialSetTag(keyFor("terracotta")) + .endsWith("TERRACOTTA") + .ensureSize("TERRACOTTA", 33).lock(); + + /** + * Covers both golden apples. + */ + public static final MaterialSetTag GOLDEN_APPLES = new MaterialSetTag(keyFor("golden_apples")) + .endsWith("GOLDEN_APPLE") + .ensureSize("GOLDEN_APPLES", 2).lock(); + + /** + * Covers the variants of horse armor. + */ + public static final MaterialSetTag HORSE_ARMORS = new MaterialSetTag(keyFor("horse_armors")) + .endsWith("_HORSE_ARMOR") + .ensureSize("HORSE_ARMORS", 4).lock(); + + /** + * Covers the variants of infested blocks. + */ + public static final MaterialSetTag INFESTED_BLOCKS = new MaterialSetTag(keyFor("infested_blocks")) + .startsWith("INFESTED_") + .ensureSize("INFESTED_BLOCKS", 7).lock(); + + /** + * Covers the variants of mushroom blocks. + */ + public static final MaterialSetTag MUSHROOM_BLOCKS = new MaterialSetTag(keyFor("mushroom_blocks")) + .endsWith("MUSHROOM_BLOCK") + .add(Material.MUSHROOM_STEM) + .ensureSize("MUSHROOM_BLOCKS", 3).lock(); + + /** + * Covers all mushrooms. + */ + public static final MaterialSetTag MUSHROOMS = new MaterialSetTag(keyFor("mushrooms")) + .add(Material.BROWN_MUSHROOM, Material.RED_MUSHROOM).lock(); + + /** + * Covers all music disc items. + */ + public static final MaterialSetTag MUSIC_DISCS = new MaterialSetTag(keyFor("music_discs")) + .startsWith("MUSIC_DISC_").lock(); + + /** + * Covers all ores. + */ + public static final MaterialSetTag ORES = new MaterialSetTag(keyFor("ores")) + .add(Material.ANCIENT_DEBRIS) + .endsWith("_ORE") + .ensureSize("ORES", 19).lock(); + + /** + * Covers all piston typed items and blocks including the piston head and moving piston. + */ + public static final MaterialSetTag PISTONS = new MaterialSetTag(keyFor("pistons")) + .contains("PISTON") + .ensureSize("PISTONS", 4).lock(); + + /** + * Covers all potato items. + */ + public static final MaterialSetTag POTATOES = new MaterialSetTag(keyFor("potatoes")) + .endsWith("POTATO") + .ensureSize("POTATOES", 3).lock(); + + /** + * Covers all wooden pressure plates and the weighted pressure plates and the stone pressure plate. + */ + public static final MaterialSetTag PRESSURE_PLATES = new MaterialSetTag(keyFor("pressure_plates")) + .endsWith("_PRESSURE_PLATE") + .ensureSize("PRESSURE_PLATES", 15).lock(); + + /** + * Covers the variants of prismarine blocks. + */ + public static final MaterialSetTag PRISMARINE = new MaterialSetTag(keyFor("prismarine")) + .add(Material.PRISMARINE, Material.PRISMARINE_BRICKS, Material.DARK_PRISMARINE).lock(); + + /** + * Covers the variants of prismarine slabs. + */ + public static final MaterialSetTag PRISMARINE_SLABS = new MaterialSetTag(keyFor("prismarine_slabs")) + .add(Material.PRISMARINE_SLAB, Material.PRISMARINE_BRICK_SLAB, Material.DARK_PRISMARINE_SLAB).lock(); + + /** + * Covers the variants of prismarine stairs. + */ + public static final MaterialSetTag PRISMARINE_STAIRS = new MaterialSetTag(keyFor("prismarine_stairs")) + .add(Material.PRISMARINE_STAIRS, Material.PRISMARINE_BRICK_STAIRS, Material.DARK_PRISMARINE_STAIRS).lock(); + + /** + * Covers the variants of pumpkins. + */ + public static final MaterialSetTag PUMPKINS = new MaterialSetTag(keyFor("pumpkins")) + .add(Material.CARVED_PUMPKIN, Material.JACK_O_LANTERN, Material.PUMPKIN).lock(); + + /** + * Covers the variants of quartz blocks. + */ + public static final MaterialSetTag QUARTZ_BLOCKS = new MaterialSetTag(keyFor("quartz_blocks")) + .add(Material.QUARTZ_BLOCK, Material.QUARTZ_PILLAR, Material.CHISELED_QUARTZ_BLOCK, Material.SMOOTH_QUARTZ).lock(); + + /** + * Covers all uncooked fish items. + */ + public static final MaterialSetTag RAW_FISH = new MaterialSetTag(keyFor("raw_fish")) + .add(Material.COD, Material.PUFFERFISH, Material.SALMON, Material.TROPICAL_FISH).lock(); + + /** + * Covers the variants of red sandstone blocks. + */ + public static final MaterialSetTag RED_SANDSTONES = new MaterialSetTag(keyFor("red_sandstones")) + .endsWith("RED_SANDSTONE") + .ensureSize("RED_SANDSTONES", 4).lock(); + + /** + * Covers the variants of sandstone blocks. + */ + public static final MaterialSetTag SANDSTONES = new MaterialSetTag(keyFor("sandstones")) + .add(Material.SANDSTONE, Material.CHISELED_SANDSTONE, Material.CUT_SANDSTONE, Material.SMOOTH_SANDSTONE).lock(); + + /** + * Covers sponge and wet sponge. + */ + public static final MaterialSetTag SPONGES = new MaterialSetTag(keyFor("sponges")) + .endsWith("SPONGE") + .ensureSize("SPONGES", 2).lock(); + + /** + * Covers the non-colored and colored shulker boxes. + */ + public static final MaterialSetTag SHULKER_BOXES = new MaterialSetTag(keyFor("shulker_boxes")) + .endsWith("SHULKER_BOX") + .ensureSize("SHULKER_BOXES", 17).lock(); + + /** + * Covers zombie, creeper, skeleton, dragon, and player heads. + */ + public static final MaterialSetTag SKULLS = new MaterialSetTag(keyFor("skulls")) + .endsWith("_HEAD") + .endsWith("_SKULL") + .not(Material.PISTON_HEAD) + .ensureSize("SKULLS", 14).lock(); + + /** + * Covers all spawn egg items. + */ + public static final MaterialSetTag SPAWN_EGGS = new MaterialSetTag(keyFor("spawn_eggs")) + .endsWith("_SPAWN_EGG") + .ensureSize("SPAWN_EGGS", 80).lock(); + + /** + * Covers all colors of stained glass. + */ + public static final MaterialSetTag STAINED_GLASS = new MaterialSetTag(keyFor("stained_glass")) + .endsWith("_STAINED_GLASS") + .ensureSize("STAINED_GLASS", 16).lock(); + + /** + * Covers all colors of stained glass panes. + */ + public static final MaterialSetTag STAINED_GLASS_PANES = new MaterialSetTag(keyFor("stained_glass_panes")) + .endsWith("STAINED_GLASS_PANE") + .ensureSize("STAINED_GLASS_PANES", 16).lock(); + + /** + * Covers all variants of trapdoors. + */ + public static final MaterialSetTag TRAPDOORS = new MaterialSetTag(keyFor("trapdoors")) + .endsWith("_TRAPDOOR") + .ensureSize("TRAPDOORS", 20).lock(); + + /** + * Covers all wood variants of doors. + */ + public static final MaterialSetTag WOODEN_DOORS = new MaterialSetTag(keyFor("wooden_doors")) + .endsWith("_DOOR") + .not(Material.IRON_DOOR) + .notContains("COPPER") + .ensureSize("WOODEN_DOORS", 11).lock(); + + /** + * Covers all wood variants of fences. + */ + public static final MaterialSetTag WOODEN_FENCES = new MaterialSetTag(keyFor("wooden_fences")) + .endsWith("_FENCE") + .not(Material.NETHER_BRICK_FENCE) + .ensureSize("WOODEN_FENCES", 11).lock(); + + /** + * Covers all wood variants of trapdoors. + */ + public static final MaterialSetTag WOODEN_TRAPDOORS = new MaterialSetTag(keyFor("wooden_trapdoors")) + .endsWith("_TRAPDOOR") + .not(Material.IRON_TRAPDOOR) + .notContains("COPPER") + .ensureSize("WOODEN_TRAPDOORS", 11).lock(); + + /** + * Covers the wood variants of gates. + */ + public static final MaterialSetTag WOODEN_GATES = new MaterialSetTag(keyFor("wooden_gates")) + .endsWith("_GATE") + .ensureSize("WOODEN_GATES", 11).lock(); + + /** + * Covers the variants of purpur. + */ + public static final MaterialSetTag PURPUR = new MaterialSetTag(keyFor("purpur")) + .startsWith("PURPUR_") + .ensureSize("PURPUR", 4).lock(); + + /** + * Covers the variants of signs. + */ + public static final MaterialSetTag SIGNS = new MaterialSetTag(keyFor("signs")) + .endsWith("_SIGN") + .ensureSize("SIGNS", 44).lock(); + + /** + * Covers the variants of a regular torch. + */ + public static final MaterialSetTag TORCH = new MaterialSetTag(keyFor("torch")) + .add(Material.TORCH, Material.WALL_TORCH) + .ensureSize("TORCH", 2).lock(); + + /** + * Covers the variants of a redstone torch. + */ + public static final MaterialSetTag REDSTONE_TORCH = new MaterialSetTag(keyFor("redstone_torch")) + .add(Material.REDSTONE_TORCH, Material.REDSTONE_WALL_TORCH) + .ensureSize("REDSTONE_TORCH", 2).lock(); + + /** + * Covers the variants of a soul torch. + */ + public static final MaterialSetTag SOUL_TORCH = new MaterialSetTag(keyFor("soul_torch")) + .add(Material.SOUL_TORCH, Material.SOUL_WALL_TORCH) + .ensureSize("SOUL_TORCH", 2).lock(); + + /** + * Covers the variants of torches. + */ + public static final MaterialSetTag TORCHES = new MaterialSetTag(keyFor("torches")) + .add(TORCH, REDSTONE_TORCH, SOUL_TORCH) + .ensureSize("TORCHES", 6).lock(); + + /** + * Covers the variants of lanterns. + */ + public static final MaterialSetTag LANTERNS = new MaterialSetTag(keyFor("lanterns")) + .add(Material.LANTERN, Material.SOUL_LANTERN) + .ensureSize("LANTERNS", 2).lock(); + + /** + * Covers the variants of rails. + */ + public static final MaterialSetTag RAILS = new MaterialSetTag(keyFor("rails")) + .endsWith("RAIL") + .ensureSize("RAILS", 4).lock(); + + /** + * Covers the variants of swords. + */ + public static final MaterialSetTag SWORDS = new MaterialSetTag(keyFor("swords")) + .endsWith("_SWORD") + .ensureSize("SWORDS", 6).lock(); + + /** + * Covers the variants of shovels. + */ + public static final MaterialSetTag SHOVELS = new MaterialSetTag(keyFor("shovels")) + .endsWith("_SHOVEL") + .ensureSize("SHOVELS", 6).lock(); + + /** + * Covers the variants of pickaxes. + */ + public static final MaterialSetTag PICKAXES = new MaterialSetTag(keyFor("pickaxes")) + .endsWith("_PICKAXE") + .ensureSize("PICKAXES", 6).lock(); + + /** + * Covers the variants of axes. + */ + public static final MaterialSetTag AXES = new MaterialSetTag(keyFor("axes")) + .endsWith("_AXE") + .ensureSize("AXES", 6).lock(); + + /** + * Covers the variants of hoes. + */ + public static final MaterialSetTag HOES = new MaterialSetTag(keyFor("hoes")) + .endsWith("_HOE") + .ensureSize("HOES", 6).lock(); + + /** + * Covers the variants of helmets. + */ + public static final MaterialSetTag HELMETS = new MaterialSetTag(keyFor("helmets")) + .endsWith("_HELMET") + .ensureSize("HELMETS", 7).lock(); + + /** + * Covers the variants of items that can be equipped in the helmet slot. + */ + public static final MaterialSetTag HEAD_EQUIPPABLE = new MaterialSetTag(keyFor("head_equippable")) + .endsWith("_HELMET") + .add(SKULLS) + .add(Material.CARVED_PUMPKIN) + .ensureSize("HEAD_EQUIPPABLE", 22).lock(); + + /** + * Covers the variants of chestplate. + */ + public static final MaterialSetTag CHESTPLATES = new MaterialSetTag(keyFor("chestplates")) + .endsWith("_CHESTPLATE") + .ensureSize("CHESTPLATES", 6).lock(); + + /** + * Covers the variants of items that can be equipped in the chest slot. + */ + public static final MaterialSetTag CHEST_EQUIPPABLE = new MaterialSetTag(keyFor("chest_equippable")) + .endsWith("_CHESTPLATE") + .add(Material.ELYTRA) + .ensureSize("CHEST_EQUIPPABLE", 7).lock(); + + /** + * Covers the variants of leggings. + */ + public static final MaterialSetTag LEGGINGS = new MaterialSetTag(keyFor("leggings")) + .endsWith("_LEGGINGS") + .ensureSize("LEGGINGS", 6).lock(); + + /** + * Covers the variants of boots. + */ + public static final MaterialSetTag BOOTS = new MaterialSetTag(keyFor("boots")) + .endsWith("_BOOTS") + .ensureSize("BOOTS", 6).lock(); + + /** + * Covers all variants of armor. + */ + public static final MaterialSetTag ARMOR = new MaterialSetTag(keyFor("armor")).add(HELMETS, CHESTPLATES, LEGGINGS, BOOTS) + .ensureSize("ARMOR", 25).lock(); + + /** + * Covers the variants of bows. + */ + public static final MaterialSetTag BOWS = new MaterialSetTag(keyFor("bows")) + .add(Material.BOW) + .add(Material.CROSSBOW) + .ensureSize("BOWS", 2).lock(); + + /** + * Covers the variants of player-throwable projectiles (not requiring a bow or any other "assistance"). + */ + public static final MaterialSetTag THROWABLE_PROJECTILES = new MaterialSetTag(keyFor("throwable_projectiles")) + .add(Material.EGG, Material.SNOWBALL, Material.SPLASH_POTION, Material.TRIDENT, Material.ENDER_PEARL, Material.EXPERIENCE_BOTTLE, Material.FIREWORK_ROCKET).lock(); + + /** + * Covers materials that can be colored, such as wool, shulker boxes, stained glass etc. + */ + @SuppressWarnings("unchecked") + public static final MaterialSetTag COLORABLE = new MaterialSetTag(keyFor("colorable")) + .add(Tag.WOOL, Tag.CARPETS).add(SHULKER_BOXES, STAINED_GLASS, STAINED_GLASS_PANES, CONCRETES, BEDS).lock(); + //.ensureSize("COLORABLE", 81).lock(); unit test don't have the vanilla item tags, so counts don't line up for real + + /** + * Covers the variants of coral. + */ + public static final MaterialSetTag CORAL = new MaterialSetTag(keyFor("coral")) + .endsWith("_CORAL") + .ensureSize("CORAL", 10).lock(); + + /** + * Covers the variants of coral fans. + */ + public static final MaterialSetTag CORAL_FANS = new MaterialSetTag(keyFor("coral_fans")) + .endsWith("_CORAL_FAN") + .endsWith("_CORAL_WALL_FAN") + .ensureSize("CORAL_FANS", 20).lock(); + + /** + * Covers the variants of coral blocks. + */ + public static final MaterialSetTag CORAL_BLOCKS = new MaterialSetTag(keyFor("coral_blocks")) + .endsWith("_CORAL_BLOCK") + .ensureSize("CORAL_BLOCKS", 10).lock(); + + /** + * Covers all items that can be enchanted from the enchantment table or anvil. + */ + public static final MaterialSetTag ENCHANTABLE = new MaterialSetTag(keyFor("enchantable")) + .add(PICKAXES, SWORDS, SHOVELS, AXES, HOES, HELMETS, CHEST_EQUIPPABLE, LEGGINGS, BOOTS, BOWS) + .add(Material.TRIDENT, Material.SHIELD, Material.FISHING_ROD, Material.SHEARS, + Material.FLINT_AND_STEEL, Material.CARROT_ON_A_STICK, Material.WARPED_FUNGUS_ON_A_STICK, + Material.BRUSH, Material.CARVED_PUMPKIN, Material.COMPASS, Material.SKELETON_SKULL, + Material.WITHER_SKELETON_SKULL, Material.PLAYER_HEAD, Material.ZOMBIE_HEAD, + Material.CREEPER_HEAD, Material.DRAGON_HEAD, Material.PIGLIN_HEAD) + .ensureSize("ENCHANTABLE", 75).lock(); + + /** + * Covers the variants of raw ores. + */ + public static final MaterialSetTag RAW_ORES = new MaterialSetTag(keyFor("raw_ores")) + .add(Material.RAW_COPPER, Material.RAW_GOLD, Material.RAW_IRON).lock(); + + /** + * Covers the variants of deepslate ores. + */ + public static final MaterialSetTag DEEPSLATE_ORES = new MaterialSetTag(keyFor("deepslate_ores")) + .add(material -> material.name().startsWith("DEEPSLATE_") && material.name().endsWith("_ORE")) + .ensureSize("DEEPSLATE_ORES", 8).lock(); + + /** + * Covers the variants of raw ore blocks. + */ + public static final MaterialSetTag RAW_ORE_BLOCKS = new MaterialSetTag(keyFor("raw_ore_blocks")) + .add(Material.RAW_COPPER_BLOCK, Material.RAW_GOLD_BLOCK, Material.RAW_IRON_BLOCK).lock(); + + /** + * Covers all oxidized copper blocks. + */ + public static final MaterialSetTag OXIDIZED_COPPER_BLOCKS = new MaterialSetTag(keyFor("oxidized_copper_blocks")) + .startsWith("OXIDIZED_").startsWith("WAXED_OXIDIZED_").ensureSize("OXIDIZED_COPPER_BLOCKS", 18).lock(); + + /** + * Covers all weathered copper blocks. + */ + public static final MaterialSetTag WEATHERED_COPPER_BLOCKS = new MaterialSetTag(keyFor("weathered_copper_blocks")) + .startsWith("WEATHERED_").startsWith("WAXED_WEATHERED_").ensureSize("WEATHERED_COPPER_BLOCKS", 18).lock(); + + /** + * Covers all exposed copper blocks. + */ + public static final MaterialSetTag EXPOSED_COPPER_BLOCKS = new MaterialSetTag(keyFor("exposed_copper_blocks")) + .startsWith("EXPOSED_").startsWith("WAXED_EXPOSED_").ensureSize("EXPOSED_COPPER_BLOCKS", 18).lock(); + + /** + * Covers all un-weathered copper blocks. + */ + public static final MaterialSetTag UNAFFECTED_COPPER_BLOCKS = new MaterialSetTag(keyFor("unaffected_copper_blocks")) + .startsWith("CUT_COPPER").startsWith("WAXED_CUT_COPPER") + .startsWith("WAXED_COPPER_").startsWith("COPPER_") + .add(Material.CHISELED_COPPER, Material.WAXED_CHISELED_COPPER) + .not(Material.COPPER_INGOT, Material.COPPER_ORE) + .ensureSize("UNAFFECTED_COPPER_BLOCKS", 18).lock(); + + /** + * Covers all waxed copper blocks. + *

+ * Combine with other copper-related tags to filter is-waxed or not. + */ + public static final MaterialSetTag WAXED_COPPER_BLOCKS = new MaterialSetTag(keyFor("waxed_copper_blocks")) + .add(m -> m.name().startsWith("WAXED_") && m.name().contains("COPPER")).ensureSize("WAXED_COPPER_BLOCKS", 36).lock(); + + /** + * Covers all un-waxed copper blocks. + *

+ * Combine with other copper-related tags to filter is-un-waxed or not. + */ + public static final MaterialSetTag UNWAXED_COPPER_BLOCKS = new MaterialSetTag(keyFor("unwaxed_copper_blocks")) + .startsWith("EXPOSED_").startsWith("WEATHERED_").startsWith("OXIDIZED_") + .startsWith("CUT_COPPER") + .add(Material.COPPER_BLOCK, Material.CHISELED_COPPER, Material.COPPER_DOOR, Material.COPPER_TRAPDOOR, Material.COPPER_GRATE, Material.COPPER_BULB) + .ensureSize("UNWAXED_COPPER_BLOCKS", 36).lock(); + + /** + * Covers all copper block variants. + */ + public static final MaterialSetTag COPPER_BLOCKS = new MaterialSetTag(keyFor("copper_blocks")) + .add(WAXED_COPPER_BLOCKS).add(UNWAXED_COPPER_BLOCKS).ensureSize("COPPER_BLOCKS", 72).lock(); + + /** + * Covers all weathering/waxed states of the plain copper block. + */ + public static final MaterialSetTag FULL_COPPER_BLOCKS = new MaterialSetTag(keyFor("full_copper_blocks")) + .endsWith("OXIDIZED_COPPER") + .endsWith("WEATHERED_COPPER") + .endsWith("EXPOSED_COPPER") + .endsWith("COPPER_BLOCK") + .not(Material.RAW_COPPER_BLOCK) + .ensureSize("FULL_COPPER_BLOCKS", 8).lock(); + + /** + * Covers all weathering/waxed states of the cut copper block. + */ + public static final MaterialSetTag CUT_COPPER_BLOCKS = new MaterialSetTag(keyFor("cut_copper_blocks")) + .endsWith("CUT_COPPER").ensureSize("CUT_COPPER_BLOCKS", 8).lock(); + + /** + * Covers all weathering/waxed states of the cut copper stairs. + */ + public static final MaterialSetTag CUT_COPPER_STAIRS = new MaterialSetTag(keyFor("cut_copper_stairs")) + .endsWith("CUT_COPPER_STAIRS").ensureSize("CUT_COPPER_STAIRS", 8).lock(); + + /** + * Covers all weathering/waxed states of the cut copper slab. + */ + public static final MaterialSetTag CUT_COPPER_SLABS = new MaterialSetTag(keyFor("cut_copper_slabs")) + .endsWith("CUT_COPPER_SLAB").ensureSize("CUT_COPPER_SLABS", 8).lock(); + + /** + * Covers all Wooden Tools. + */ + public static final MaterialSetTag WOODEN_TOOLS = new MaterialSetTag(keyFor("wooden_tools")) + .add(Material.WOODEN_AXE, Material.WOODEN_HOE, Material.WOODEN_PICKAXE, Material.WOODEN_SHOVEL, Material.WOODEN_SWORD) + .ensureSize("WOODEN_TOOLS", 5).lock(); + + /** + * Covers all Stone Tools. + */ + public static final MaterialSetTag STONE_TOOLS = new MaterialSetTag(keyFor("stone_tools")) + .add(Material.STONE_AXE, Material.STONE_HOE, Material.STONE_PICKAXE, Material.STONE_SHOVEL, Material.STONE_SWORD) + .ensureSize("STONE_TOOLS", 5).lock(); + + /** + * Covers all Iron Tools. + */ + public static final MaterialSetTag IRON_TOOLS = new MaterialSetTag(keyFor("iron_tools")) + .add(Material.IRON_AXE, Material.IRON_HOE, Material.IRON_PICKAXE, Material.IRON_SHOVEL, Material.IRON_SWORD) + .ensureSize("IRON_TOOLS", 5).lock(); + + /** + * Covers all Gold Tools. + */ + public static final MaterialSetTag GOLDEN_TOOLS = new MaterialSetTag(keyFor("golden_tools")) + .add(Material.GOLDEN_AXE, Material.GOLDEN_HOE, Material.GOLDEN_PICKAXE, Material.GOLDEN_SHOVEL, Material.GOLDEN_SWORD) + .ensureSize("GOLDEN_TOOLS", 5).lock(); + + /** + * Covers all Diamond Tools. + */ + public static final MaterialSetTag DIAMOND_TOOLS = new MaterialSetTag(keyFor("diamond_tools")) + .add(Material.DIAMOND_AXE, Material.DIAMOND_HOE, Material.DIAMOND_PICKAXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_SWORD) + .ensureSize("DIAMOND_TOOLS", 5).lock(); + + /** + * Covers all Netherite Tools. + */ + public static final MaterialSetTag NETHERITE_TOOLS = new MaterialSetTag(keyFor("netherite_tools")) + .add(Material.NETHERITE_AXE, Material.NETHERITE_HOE, Material.NETHERITE_PICKAXE, Material.NETHERITE_SHOVEL, Material.NETHERITE_SWORD) + .ensureSize("NETHERITE_TOOLS", 5).lock(); + +} diff --git a/src/main/java/io/papermc/paper/tag/BaseTag.java b/src/main/java/io/papermc/paper/tag/BaseTag.java new file mode 100644 index 0000000000000000000000000000000000000000..794787912325ae32b3cfc8217bc3fc2159ceabd5 --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/BaseTag.java @@ -0,0 +1,181 @@ +package io.papermc.paper.tag; + +import com.google.common.collect.Lists; +import java.util.Collections; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public abstract class BaseTag> implements Tag { + + protected final NamespacedKey key; + protected final Set tagged; + private final List> globalPredicates; + private boolean locked = false; + + public BaseTag(@NotNull Class clazz, @NotNull NamespacedKey key, @NotNull Predicate filter) { + this(clazz, key); + add(filter); + } + + public BaseTag(@NotNull Class clazz, @NotNull NamespacedKey key, @NotNull T...values) { + this(clazz, key, Lists.newArrayList(values)); + } + + public BaseTag(@NotNull Class clazz, @NotNull NamespacedKey key, @NotNull Collection values) { + this(clazz, key, values, o -> true); + } + + public BaseTag(@NotNull Class clazz, @NotNull NamespacedKey key, @NotNull Collection values, @NotNull Predicate... globalPredicates) { + this.key = key != null ? key : NamespacedKey.randomKey(); + this.tagged = clazz.isEnum() ? createEnumSet(clazz) : new HashSet<>(); + this.tagged.addAll(values); + this.globalPredicates = Lists.newArrayList(globalPredicates); + } + + private Set createEnumSet(Class enumClass) { + assert enumClass.isEnum(); + return (Set) EnumSet.noneOf((Class) enumClass); + } + + public @NotNull C lock() { + this.locked = true; + return (C) this; + } + + public boolean isLocked() { + return this.locked; + } + + private void checkLock() { + if (this.locked) { + throw new UnsupportedOperationException("Tag (" + this.key + ") is locked"); + } + } + + @NotNull + @Override + public NamespacedKey getKey() { + return key; + } + + @NotNull + @Override + public Set getValues() { + return Collections.unmodifiableSet(tagged); + } + + @Override + public boolean isTagged(@NotNull T item) { + return tagged.contains(item); + } + + @NotNull + public C add(@NotNull Tag...tags) { + for (Tag tag : tags) { + add(tag.getValues()); + } + return (C) this; + } + + @NotNull + public C add(@NotNull T...values) { + this.checkLock(); + this.tagged.addAll(Lists.newArrayList(values)); + return (C) this; + } + + @NotNull + public C add(@NotNull Collection collection) { + this.checkLock(); + this.tagged.addAll(collection); + return (C) this; + } + + @NotNull + public C add(@NotNull Predicate filter) { + return add(getAllPossibleValues().stream().filter(globalPredicates.stream().reduce(Predicate::or).orElse(t -> true)).filter(filter).collect(Collectors.toSet())); + } + + @NotNull + public C contains(@NotNull String with) { + return add(value -> getName(value).contains(with)); + } + + @NotNull + public C endsWith(@NotNull String with) { + return add(value -> getName(value).endsWith(with)); + } + + @NotNull + public C startsWith(@NotNull String with) { + return add(value -> getName(value).startsWith(with)); + } + + @NotNull + public C not(@NotNull Tag...tags) { + for (Tag tag : tags) { + not(tag.getValues()); + } + return (C) this; + } + + @NotNull + public C not(@NotNull T...values) { + this.checkLock(); + this.tagged.removeAll(Lists.newArrayList(values)); + return (C) this; + } + + @NotNull + public C not(@NotNull Collection values) { + this.checkLock(); + this.tagged.removeAll(values); + return (C) this; + } + + @NotNull + public C not(@NotNull Predicate filter) { + not(getAllPossibleValues().stream().filter(globalPredicates.stream().reduce(Predicate::or).orElse(t -> true)).filter(filter).collect(Collectors.toSet())); + return (C) this; + } + + @NotNull + public C notContains(@NotNull String with) { + return not(value -> getName(value).contains(with)); + } + + @NotNull + public C notEndsWith(@NotNull String with) { + return not(value -> getName(value).endsWith(with)); + } + + @NotNull + public C notStartsWith(@NotNull String with) { + return not(value -> getName(value).startsWith(with)); + } + + @NotNull + public C ensureSize(@NotNull String label, int size) { + long actual = this.tagged.stream().filter(globalPredicates.stream().reduce(Predicate::or).orElse(t -> true)).count(); + if (size != actual) { + throw new IllegalStateException(key.toString() + ": " + label + " - Expected " + size + " values, got " + actual); + } + return (C) this; + } + + @NotNull + protected abstract Set getAllPossibleValues(); + + @NotNull + protected abstract String getName(@NotNull T value); +} diff --git a/src/main/java/io/papermc/paper/tag/EntitySetTag.java b/src/main/java/io/papermc/paper/tag/EntitySetTag.java new file mode 100644 index 0000000000000000000000000000000000000000..c89c4619aaf388197834d98eb95af2f1e93db871 --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/EntitySetTag.java @@ -0,0 +1,42 @@ +package io.papermc.paper.tag; + +import org.bukkit.NamespacedKey; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class EntitySetTag extends BaseTag { + + public EntitySetTag(@NotNull NamespacedKey key, @NotNull Predicate filter) { + super(EntityType.class, key, filter); + } + + public EntitySetTag(@NotNull NamespacedKey key, @NotNull EntityType... values) { + super(EntityType.class, key, values); + } + + public EntitySetTag(@NotNull NamespacedKey key, @NotNull Collection values) { + super(EntityType.class, key, values); + } + + public EntitySetTag(@NotNull NamespacedKey key, @NotNull Collection values, @NotNull Predicate... globalPredicates) { + super(EntityType.class, key, values, globalPredicates); + } + + @NotNull + @Override + protected Set getAllPossibleValues() { + return Stream.of(EntityType.values()).collect(Collectors.toSet()); + } + + @NotNull + @Override + protected String getName(@NotNull EntityType value) { + return value.name(); + } +} diff --git a/src/main/java/io/papermc/paper/tag/EntityTags.java b/src/main/java/io/papermc/paper/tag/EntityTags.java new file mode 100644 index 0000000000000000000000000000000000000000..5376e51adf5af15572b0d760b17f2de6d3f24a9b --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/EntityTags.java @@ -0,0 +1,54 @@ +package io.papermc.paper.tag; + +import org.bukkit.NamespacedKey; + +import static org.bukkit.entity.EntityType.*; + +/** + * All tags in this class are unmodifiable, attempting to modify them will throw an + * {@link UnsupportedOperationException}. + */ +public class EntityTags { + + private static NamespacedKey keyFor(String key) { + //noinspection deprecation + return new NamespacedKey("paper", key + "_settag"); + } + + /** + * Covers undead mobs + * @see https://minecraft.wiki/wiki/Mob#Undead_mobs + */ + public static final EntitySetTag UNDEADS = new EntitySetTag(keyFor("undeads")) + .add(DROWNED, HUSK, PHANTOM, SKELETON, SKELETON_HORSE, STRAY, WITHER, WITHER_SKELETON, ZOGLIN, ZOMBIE, ZOMBIE_HORSE, ZOMBIE_VILLAGER, ZOMBIFIED_PIGLIN) + .ensureSize("UNDEADS", 13).lock(); + + /** + * Covers all horses + */ + public static final EntitySetTag HORSES = new EntitySetTag(keyFor("horses")) + .contains("HORSE") + .ensureSize("HORSES", 3).lock(); + + /** + * Covers all minecarts + */ + public static final EntitySetTag MINECARTS = new EntitySetTag(keyFor("minecarts")) + .contains("MINECART") + .ensureSize("MINECARTS", 7).lock(); + + /** + * Covers mobs that split into smaller mobs + */ + public static final EntitySetTag SPLITTING_MOBS = new EntitySetTag(keyFor("splitting_mobs")) + .add(SLIME, MAGMA_CUBE) + .ensureSize("SLIMES", 2).lock(); + + /** + * Covers all water based mobs + * @see https://minecraft.wiki/wiki/Mob#Aquatic_mobs + */ + public static final EntitySetTag WATER_BASED = new EntitySetTag(keyFor("water_based")) + .add(AXOLOTL, DOLPHIN, SQUID, GLOW_SQUID, GUARDIAN, ELDER_GUARDIAN, TURTLE, COD, SALMON, PUFFERFISH, TROPICAL_FISH) + .ensureSize("WATER_BASED", 11).lock(); +} diff --git a/src/main/java/org/bukkit/Tag.java b/src/main/java/org/bukkit/Tag.java index 1be862110e94654be12fcef5980388abf242ac7f..298ae1294a122705bca6bd74ea540185839d1ed5 100644 --- a/src/main/java/org/bukkit/Tag.java +++ b/src/main/java/org/bukkit/Tag.java @@ -11,6 +11,10 @@ import org.jetbrains.annotations.NotNull; * Note that whilst all tags defined within this interface must be present in * implementations, their existence is not guaranteed across future versions. * + *

Custom tags defined by Paper are not present (as constants) in this class. + * To access them please refer to {@link com.destroystokyo.paper.MaterialTags} + * and {@link io.papermc.paper.tag.EntityTags}.

+ * * @param the type of things grouped by this tag */ public interface Tag extends Keyed { diff --git a/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java b/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..82ee9a486d1c3deec6e529b1cb12ecc2c6a5862e --- /dev/null +++ b/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License + */ + +package com.destroystokyo.paper; + +import io.papermc.paper.tag.BaseTag; +import io.papermc.paper.tag.EntityTags; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import org.bukkit.Bukkit; +import org.bukkit.support.AbstractTestingBase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MaterialTagsTest extends AbstractTestingBase { + + @Test + public void testInitialize() { + try { + MaterialTags.SHULKER_BOXES.getValues(); + assert true; + } catch (Throwable e) { + Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e); + assert false; + } + } + + @Test + public void testLocked() { + testLocked(MaterialTags.class); + testLocked(EntityTags.class); + } + + private static void testLocked(Class clazz) { + for (BaseTag tag : collectTags(clazz)) { + assertTrue(tag.isLocked(), "Tag " + tag.key() + " is not locked"); + } + } + + private static Set> collectTags(Class clazz) { + Set> tags = new HashSet<>(); + try { + for (Field field : clazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && BaseTag.class.isAssignableFrom(field.getType())) { + tags.add((BaseTag) field.get(null)); + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return tags; + } +} diff --git a/src/test/java/io/papermc/paper/EntityTagsTest.java b/src/test/java/io/papermc/paper/EntityTagsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..91d70cc703192b5a3a48211ee0dadd2e4260ec00 --- /dev/null +++ b/src/test/java/io/papermc/paper/EntityTagsTest.java @@ -0,0 +1,21 @@ +package io.papermc.paper; + +import io.papermc.paper.tag.EntityTags; +import java.util.logging.Level; +import org.bukkit.Bukkit; +import org.bukkit.support.AbstractTestingBase; +import org.junit.jupiter.api.Test; + +public class EntityTagsTest extends AbstractTestingBase { + + @Test + public void testInitialize() { + try { + EntityTags.HORSES.getValues(); + assert true; + } catch (Throwable e) { + Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e); + assert false; + } + } +}