Paper/patches/api/0156-Add-Material-Tags.patch
Nassim Jahnke 928bcc8d3a
Updated Upstream (Bukkit/CraftBukkit) (#8430)
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
09943450 Update SnakeYAML version
5515734f SPIGOT-7162: Incorrect description for Entity#getVehicle javadoc
6f82b381 PR-788: Add getHand() to all relevant events

CraftBukkit Changes:
aaf484f6f SPIGOT-7163: CraftMerchantRecipe doesn't copy demand and specialPrice from BukkitMerchantRecipe
5329dd6fd PR-1107: Add getHand() to all relevant events
93061706e SPIGOT-7045: Ocelots never spawn with babies with spawn reason OCELOT_BABY
2022-10-02 09:56:36 +02:00

1212 lines
43 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 <jake.m.potrebic@gmail.com>
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<Material, MaterialSetTag> {
+
+ /**
+ * @deprecated Use NamespacedKey version of constructor
+ */
+ @Deprecated
+ public MaterialSetTag(@NotNull Predicate<Material> filter) {
+ this(null, Stream.of(Material.values()).filter(filter).collect(Collectors.toList()));
+ }
+
+ /**
+ * @deprecated Use NamespacedKey version of constructor
+ */
+ @Deprecated
+ public MaterialSetTag(@NotNull Collection<Material> 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<Material> 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<Material> materials) {
+ this(key != null ? key : NamespacedKey.randomKey(), materials, ((Predicate<Material>) Material::isLegacy).negate());
+ }
+
+ public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection<Material> materials, @NotNull Predicate<Material>...globalPredicates) {
+ super(Material.class, key != null ? key : NamespacedKey.randomKey(), materials, globalPredicates);
+ }
+
+ @NotNull
+ @Override
+ protected Set<Material> 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..1a78872e26f8fadbddd9af15fff063d03690077f
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/MaterialTags.java
@@ -0,0 +1,661 @@
+/*
+ * 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.
+ * <p>
+ * 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", 10).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", 9).lock();
+
+ /**
+ * Covers all variants of fences.
+ */
+ public static final MaterialSetTag FENCES = new MaterialSetTag(keyFor("fences"))
+ .endsWith("_FENCE")
+ .ensureSize("FENCES", 10).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", 13).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", 12).lock();
+
+ /**
+ * Covers all spawn egg items.
+ */
+ public static final MaterialSetTag SPAWN_EGGS = new MaterialSetTag(keyFor("spawn_eggs"))
+ .endsWith("_SPAWN_EGG")
+ .ensureSize("SPAWN_EGGS", 71).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", 10).lock();
+
+ /**
+ * Covers all wood variants of doors.
+ */
+ public static final MaterialSetTag WOODEN_DOORS = new MaterialSetTag(keyFor("wooden_doors"))
+ .endsWith("_DOOR")
+ .not(Material.IRON_DOOR)
+ .ensureSize("WOODEN_DOORS", 9).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", 9).lock();
+
+ /**
+ * Covers all wood variants of trapdoors.
+ */
+ public static final MaterialSetTag WOODEN_TRAPDOORS = new MaterialSetTag(keyFor("wooden_trapdoors"))
+ .endsWith("_TRAPDOOR")
+ .not(Material.IRON_TRAPDOOR)
+ .ensureSize("WOODEN_TRAPDOORS", 9).lock();
+
+ /**
+ * Covers the wood variants of gates.
+ */
+ public static final MaterialSetTag WOODEN_GATES = new MaterialSetTag(keyFor("wooden_gates"))
+ .endsWith("_GATE")
+ .ensureSize("WOODEN_GATES", 9).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", 18).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("restone_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", 20).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)
+ .ensureSize("ENCHANTABLE", 65).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", 8).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", 8).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", 8).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").add(Material.COPPER_BLOCK).add(Material.WAXED_COPPER_BLOCK).ensureSize("UNAFFECTED_COPPER_BLOCKS", 8).lock();
+
+ /**
+ * Covers all waxed copper blocks.
+ * <p>
+ * 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", 16).lock();
+
+ /**
+ * Covers all un-waxed copper blocks.
+ * <p>
+ * 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"))
+ .contains("CUT_COPPER").endsWith("_COPPER").notContains("WAXED").add(Material.COPPER_BLOCK).not(Material.RAW_COPPER).ensureSize("UNWAXED_COPPER_BLOCKS", 16).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", 32).lock();
+
+ /**
+ * Covers all weathering/waxed states of the plain copper block.
+ */
+ public static final MaterialSetTag FULL_COPPER_BLOCKS = new MaterialSetTag(keyFor("full_copper_blocks"))
+ .contains("OXIDIZED_COPPER")
+ .contains("WEATHERED_COPPER")
+ .contains("EXPOSED_COPPER")
+ .contains("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();
+}
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..71484259de4783b861803c48850317eb2d60bda0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/BaseTag.java
@@ -0,0 +1,180 @@
+package io.papermc.paper.tag;
+
+import com.google.common.collect.Lists;
+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<T extends Keyed, C extends BaseTag<T, C>> implements Tag<T> {
+
+ protected final NamespacedKey key;
+ protected final Set<T> tagged;
+ private final List<Predicate<T>> globalPredicates;
+ private boolean locked = false;
+
+ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull Predicate<T> filter) {
+ this(clazz, key);
+ add(filter);
+ }
+
+ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull T...values) {
+ this(clazz, key, Lists.newArrayList(values));
+ }
+
+ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull Collection<T> values) {
+ this(clazz, key, values, o -> true);
+ }
+
+ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull Collection<T> values, @NotNull Predicate<T>... 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 <E> Set<E> createEnumSet(Class<E> enumClass) {
+ assert enumClass.isEnum();
+ return (Set<E>) EnumSet.noneOf((Class<Enum>) 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<T> getValues() {
+ return tagged;
+ }
+
+ @Override
+ public boolean isTagged(@NotNull T item) {
+ return tagged.contains(item);
+ }
+
+ @NotNull
+ public C add(@NotNull Tag<T>...tags) {
+ for (Tag<T> 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<T> collection) {
+ this.checkLock();
+ this.tagged.addAll(collection);
+ return (C) this;
+ }
+
+ @NotNull
+ public C add(@NotNull Predicate<T> 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<T>...tags) {
+ for (Tag<T> 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<T> values) {
+ this.checkLock();
+ this.tagged.removeAll(values);
+ return (C) this;
+ }
+
+ @NotNull
+ public C not(@NotNull Predicate<T> 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<T> 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<EntityType, EntitySetTag> {
+
+ public EntitySetTag(@NotNull NamespacedKey key, @NotNull Predicate<EntityType> 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<EntityType> values) {
+ super(EntityType.class, key, values);
+ }
+
+ public EntitySetTag(@NotNull NamespacedKey key, @NotNull Collection<EntityType> values, @NotNull Predicate<EntityType>... globalPredicates) {
+ super(EntityType.class, key, values, globalPredicates);
+ }
+
+ @NotNull
+ @Override
+ protected Set<EntityType> 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..d7eb49a05c3f0cacf285f8995433c5d5e988de0f
--- /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 <a href="https://minecraft.gamepedia.com/Mob#Undead_mobs">https://minecraft.gamepedia.com/Mob#Undead_mobs</a>
+ */
+ 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 <a href="https://minecraft.gamepedia.com/Mob#Water-based_mobs">https://minecraft.gamepedia.com/Mob#Water-based_mobs</a>
+ */
+ 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 6f6e359dfdba27f6550719479a2256a2b159d39e..60e98f0eb8028fc9dfb73320b2939a33435174c1 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.
*
+ * <p>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}.</p>
+ *
* @param <T> the type of things grouped by this tag
*/
public interface Tag<T extends Keyed> 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..f849d8b12a7e3d1606698408ab4bb140a3b370e4
--- /dev/null
+++ b/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 org.bukkit.Bukkit;
+import org.bukkit.TestServer;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+
+import static org.junit.Assert.assertTrue;
+
+public class MaterialTagsTest {
+
+ @Before
+ public void before() {
+ TestServer.getInstance();
+ }
+
+ @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 " + tag.key() + " is not locked", tag.isLocked());
+ }
+ }
+
+ private static Set<BaseTag<?, ?>> collectTags(Class<?> clazz) {
+ Set<BaseTag<?, ?>> 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..06bb9d1180361d3d00c699796bbacbce5bef2177
--- /dev/null
+++ b/src/test/java/io/papermc/paper/EntityTagsTest.java
@@ -0,0 +1,24 @@
+package io.papermc.paper;
+
+import com.destroystokyo.paper.MaterialTags;
+import io.papermc.paper.tag.EntityTags;
+import org.bukkit.Bukkit;
+import org.bukkit.TestServer;
+import org.junit.Test;
+
+import java.util.logging.Level;
+
+public class EntityTagsTest {
+
+ @Test
+ public void testInitialize() {
+ try {
+ TestServer.getInstance();
+ EntityTags.HORSES.getValues();
+ assert true;
+ } catch (Throwable e) {
+ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
+ assert false;
+ }
+ }
+}
diff --git a/src/test/java/org/bukkit/TestServer.java b/src/test/java/org/bukkit/TestServer.java
index 701a17c10f31cd345238a3c568264178ce372faa..bfb39f48bea14ad616c53e2a8a6901e93f728768 100644
--- a/src/test/java/org/bukkit/TestServer.java
+++ b/src/test/java/org/bukkit/TestServer.java
@@ -32,6 +32,16 @@ public final class TestServer implements InvocationHandler {
}
}
);
+ // Paper start
+ methodMap.put(
+ Server.class.getMethod("getTag", String.class, NamespacedKey.class, Class.class),
+ new MethodHandler() {
+ public Object handle(TestServer server, Object[] args) {
+ return new com.destroystokyo.paper.MaterialSetTag();
+ }
+ }
+ );
+ // Paper end
methodMap.put(
Server.class.getMethod("getPluginManager"),
new MethodHandler() {