package org.bukkit; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import io.papermc.paper.registry.TypedKey; import io.papermc.paper.registry.tag.Tag; import io.papermc.paper.registry.tag.TagKey; import java.util.Collection; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; import net.kyori.adventure.key.Key; import org.bukkit.advancement.Advancement; import org.bukkit.attribute.Attribute; import org.bukkit.block.Biome; import org.bukkit.block.BlockType; import org.bukkit.block.banner.PatternType; import org.bukkit.boss.KeyedBossBar; import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Cat; import org.bukkit.entity.EntityType; import org.bukkit.entity.Frog; import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; import org.bukkit.entity.memory.MemoryKey; import org.bukkit.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; import org.bukkit.inventory.ItemType; import org.bukkit.inventory.MenuType; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.loot.LootTables; import org.bukkit.map.MapCursor; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** * Represents a registry of Bukkit objects that may be retrieved by * {@link NamespacedKey}. * * @param type of item in the registry */ @NullMarked public interface Registry extends Iterable { private static Registry registryFor(final RegistryKey registryKey) { return RegistryAccess.registryAccess().getRegistry(registryKey); } @SuppressWarnings("removal") @Deprecated(forRemoval = true, since = "1.21.4") private static Registry legacyRegistryFor(final Class clazz) { return Objects.requireNonNull(RegistryAccess.registryAccess().getRegistry(clazz), () -> "No registry present for " + clazz.getSimpleName() + ". This is a bug."); } /** * Server advancements. * * @see Bukkit#getAdvancement(NamespacedKey) * @see Bukkit#advancementIterator() * @deprecated use {@link Bukkit#getAdvancement(NamespacedKey)} and {@link Bukkit#advancementIterator()} */ @Deprecated(since = "1.21.4", forRemoval = true) Registry ADVANCEMENT = new NotARegistry<>() { @Override public @Nullable Advancement get(final NamespacedKey key) { return Bukkit.getAdvancement(key); } @Override public Iterator iterator() { return Bukkit.advancementIterator(); } }; /** * Server art. * * @see Art * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#PAINTING_VARIANT} */ @Deprecated(since = "1.21.3") // Paper Registry ART = legacyRegistryFor(Art.class); /** * Attribute. * * @see Attribute */ Registry ATTRIBUTE = registryFor(RegistryKey.ATTRIBUTE); /** * Server banner patterns. * * @see PatternType * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#BANNER_PATTERN} */ @Deprecated(since = "1.21") // Paper Registry BANNER_PATTERN = legacyRegistryFor(PatternType.class); /** * Server biomes. * * @see Biome * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#BIOME} */ @Deprecated(since = "1.21.3") // Paper Registry BIOME = legacyRegistryFor(Biome.class); /** * Server block types. * * @see BlockType */ Registry BLOCK = registryFor(RegistryKey.BLOCK); /** * Custom boss bars. * * @see Bukkit#getBossBar(org.bukkit.NamespacedKey) * @see Bukkit#getBossBars() * @deprecated use {@link Bukkit#getBossBar(NamespacedKey)} and {@link Bukkit#getBossBars()} */ @Deprecated(since = "1.21.4", forRemoval = true) Registry BOSS_BARS = new NotARegistry<>() { @Override public @Nullable KeyedBossBar get(final NamespacedKey key) { return Bukkit.getBossBar(key); } @Override public Iterator iterator() { return Bukkit.getBossBars(); } }; /** * Server cat types. * * @see Cat.Type * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#CAT_VARIANT} */ @Deprecated(since = "1.21.5") Registry CAT_VARIANT = legacyRegistryFor(Cat.Type.class); /** * Server enchantments. * * @see Enchantment * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#ENCHANTMENT} */ @Deprecated(since = "1.21") Registry ENCHANTMENT = legacyRegistryFor(Enchantment.class); /** * Server entity types. * * @see EntityType */ Registry ENTITY_TYPE = registryFor(RegistryKey.ENTITY_TYPE); /** * Server instruments. * * @see MusicInstrument * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#INSTRUMENT} */ @Deprecated(since = "1.21.2") Registry INSTRUMENT = legacyRegistryFor(MusicInstrument.class); /** * Server item types. * * @see ItemType */ Registry ITEM = registryFor(RegistryKey.ITEM); /** * Default server loot tables. * * @see LootTables */ Registry LOOT_TABLES = new SimpleRegistry<>(LootTables.class); /** * Server materials. * * @see Material */ Registry MATERIAL = new SimpleRegistry<>(Material.class, (mat) -> !mat.isLegacy()); /** * Server menus. * * @see MenuType */ @ApiStatus.Experimental Registry MENU = registryFor(RegistryKey.MENU); /** * Server mob effects. * * @see PotionEffectType */ Registry MOB_EFFECT = registryFor(RegistryKey.MOB_EFFECT); /** * Server particles. * * @see Particle */ Registry PARTICLE_TYPE = registryFor(RegistryKey.PARTICLE_TYPE); // Paper /** * Server potions. * * @see PotionType */ Registry POTION = registryFor(RegistryKey.POTION); // Paper /** * Server statistics. * * @see Statistic */ Registry STATISTIC = new SimpleRegistry<>(Statistic.class); /** * Server structures. * * @see Structure * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#STRUCTURE} */ @Deprecated(since = "1.20.6") // Paper Registry STRUCTURE = legacyRegistryFor(Structure.class); /** * Server structure types. * * @see StructureType */ Registry STRUCTURE_TYPE = registryFor(RegistryKey.STRUCTURE_TYPE); /** * Sound events. * * @see Sound */ Registry SOUND_EVENT = registryFor(RegistryKey.SOUND_EVENT); /** * Trim materials. * * @see TrimMaterial * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#TRIM_MATERIAL} */ @Deprecated(since = "1.20.6") // Paper Registry TRIM_MATERIAL = legacyRegistryFor(TrimMaterial.class); /** * Trim patterns. * * @see TrimPattern * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#TRIM_PATTERN} */ @Deprecated(since = "1.20.6") Registry TRIM_PATTERN = legacyRegistryFor(TrimPattern.class); /** * Damage types. * * @see DamageType * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#DAMAGE_TYPE} */ @Deprecated(since = "1.20.6") Registry DAMAGE_TYPE = legacyRegistryFor(DamageType.class); /** * Jukebox songs. * * @see JukeboxSong * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#JUKEBOX_SONG} */ @Deprecated(since = "1.21") Registry JUKEBOX_SONG = legacyRegistryFor(JukeboxSong.class); /** * Villager profession. * * @see Villager.Profession */ Registry VILLAGER_PROFESSION = registryFor(RegistryKey.VILLAGER_PROFESSION); /** * Villager type. * * @see Villager.Type */ Registry VILLAGER_TYPE = registryFor(RegistryKey.VILLAGER_TYPE); /** * Memory Keys. * * @see MemoryKey */ Registry> MEMORY_MODULE_TYPE = new NotARegistry<>() { @Override public Iterator> iterator() { return MemoryKey.values().iterator(); } @Override public int size() { return MemoryKey.values().size(); } @Override public @Nullable MemoryKey get(final NamespacedKey key) { return MemoryKey.getByKey(key); } }; /** * Server fluids. * * @see Fluid */ Registry FLUID = registryFor(RegistryKey.FLUID); /** * Frog variants. * * @see Frog.Variant * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#FROG_VARIANT} */ @Deprecated(since = "1.21.5") Registry FROG_VARIANT = legacyRegistryFor(Frog.Variant.class); /** * Wolf variants. * * @see Wolf.Variant * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#WOLF_VARIANT} */ @Deprecated(since = "1.20.6") Registry WOLF_VARIANT = legacyRegistryFor(Wolf.Variant.class); /** * Map cursor types. * * @see MapCursor.Type */ Registry MAP_DECORATION_TYPE = registryFor(RegistryKey.MAP_DECORATION_TYPE); /** * Game events. * * @see GameEvent * @see io.papermc.paper.registry.event.RegistryEvents#GAME_EVENT */ Registry GAME_EVENT = registryFor(RegistryKey.GAME_EVENT); /** * Data component types. * * @see DataComponentType */ Registry DATA_COMPONENT_TYPE = registryFor(RegistryKey.DATA_COMPONENT_TYPE); // Paper // /** * @apiNote use {@link #MOB_EFFECT} instead * @hidden */ @ApiStatus.Obsolete(since = "1.21.4") Registry EFFECT = MOB_EFFECT; /** * @apiNote use {@link #MOB_EFFECT} instead * @hidden */ @ApiStatus.Obsolete(since = "1.21.4") Registry POTION_EFFECT_TYPE = EFFECT; /** * @apiNote use {@link #SOUND_EVENT} * @hidden */ @ApiStatus.Obsolete(since = "1.21.4") Registry SOUNDS = registryFor(RegistryKey.SOUND_EVENT); // /** * Get the object by its key. * * @param key non-null key * @return item or null if it does not exist */ @Nullable T get(NamespacedKey key); // Paper start /** * Get the object by its key. * * @param key non-null key * @return item or null if it does not exist */ default @Nullable T get(final Key key) { return key instanceof final NamespacedKey nsKey ? this.get(nsKey) : this.get(new NamespacedKey(key.namespace(), key.value())); } /** * Get the object by its typed key. * * @param typedKey non-null typed key * @return item or null if it does not exist */ default @Nullable T get(final TypedKey typedKey) { Preconditions.checkArgument(typedKey != null, "typedKey cannot be null"); return this.get(typedKey.key()); } // Paper end // Paper start - improve Registry /** * Gets the object by its key or throws if it doesn't exist. * * @param key the key to get the object of in this registry * @return the object for the key * @throws NoSuchElementException if the key doesn't point to an object in the registry */ default T getOrThrow(final net.kyori.adventure.key.Key key) { Preconditions.checkArgument(key != null, "key cannot be null"); final T value = this.get(key); if (value == null) { throw new NoSuchElementException("No value for " + key + " in " + this); } return value; } /** * Gets the object by its key or throws if it doesn't exist. * * @param key the key to get the object of in this registry * @return the object for the key * @throws NoSuchElementException if the key doesn't point to an object in the registry */ default T getOrThrow(final TypedKey key) { final T value = this.get(key); if (value == null) { throw new NoSuchElementException("No value for " + key + " in " + this); } return value; } /** * Gets the key for this object or throws if it doesn't exist. *

* Some types can exist without being in a registry * and such will have no key associated with them. This * method throw an exception if it isn't in this registry. * * @param value the value to get the key of in this registry * @return the key for the value * @throws NoSuchElementException if the value doesn't exist in this registry * @see #getKey(Keyed) */ default NamespacedKey getKeyOrThrow(final T value) { final NamespacedKey key = this.getKey(value); if (key == null) { throw new NoSuchElementException(value + " has no key in " + this); } return key; } /** * Get the key for this object. *

* Some types can exist without being in a registry * and such will have no key associated with them. This * method will return null. * * @param value the value to get the key of in this registry * @return the key for the value or null if not in the registry * @see #getKeyOrThrow(Keyed) */ @Nullable NamespacedKey getKey(T value); // Paper end - improve Registry // Paper start - RegistrySet API /** * Checks if this registry has a tag with the given key. * * @param key the key to check for * @return true if this registry has a tag with the given key, false otherwise * @throws UnsupportedOperationException if this registry doesn't have or support tags * @see #getTag(TagKey) */ boolean hasTag(TagKey key); /** * Gets the named registry set (tag) for the given key. * * @param key the key to get the tag for * @return the tag for the key * @throws NoSuchElementException if no tag with the given key is found * @throws UnsupportedOperationException if this registry doesn't have or support tags * @see #hasTag(TagKey) * @see #getTagValues(TagKey) */ @ApiStatus.Experimental Tag getTag(TagKey key); /** * Gets the named registry set (tag) for the given key and resolves it with this registry. * * @param key the key to get the tag for * @return the resolved values * @throws NoSuchElementException if no tag with the given key is found * @throws UnsupportedOperationException if this registry doesn't have or support tags * @see #getTag(TagKey) * @see Tag#resolve(Registry) */ @ApiStatus.Experimental default Collection getTagValues(final TagKey key) { Tag tag = this.getTag(key); return tag.resolve(this); } /** * Gets all the tags in this registry. * * @return a stream of all tags in this registry * @throws UnsupportedOperationException if this registry doesn't have or support tags */ @ApiStatus.Experimental Collection> getTags(); // Paper end - RegistrySet API /** * Get the object by its key. *

* If there is no object with the given key, an exception will be thrown. * * @param key to get the object from * @return object with the given key * @throws NoSuchElementException if there is no object with the given key */ default T getOrThrow(final NamespacedKey key) { return this.getOrThrow((Key) key); } /** * Returns a new stream, which contains all registry items, which are registered to the registry. * * @return a stream of all registry items */ Stream stream(); /** * Returns a new stream, which contains all registry keys, which are registered to the registry. * * @return a stream of all registry keys */ Stream keyStream(); /** * Attempts to match the registered object with the given key. *

* This will attempt to find a reasonable match based on the provided input * and may do so through unspecified means. * * @param input non-null input * @return registered object or null if does not exist * @deprecated this method's behavior is broken and not useful. If you want to get an object * based on its vanilla name, or a key, wrap it in a {@link NamespacedKey} object and use {@link #get(NamespacedKey)} */ // Paper @Deprecated(forRemoval = true) default @Nullable T match(final String input) { Preconditions.checkArgument(input != null, "input must not be null"); final String filtered = input.toLowerCase(Locale.ROOT).replaceAll("\\s+", "_"); final NamespacedKey namespacedKey = NamespacedKey.fromString(filtered); return (namespacedKey != null) ? this.get(namespacedKey) : null; } /** * Gets the size of the registry. * * @return the size of the registry */ int size(); @ApiStatus.Internal class SimpleRegistry & Keyed> extends NotARegistry { // Paper - remove final private final Class type; private final Map map; protected SimpleRegistry(final Class type) { this(type, Predicates.alwaysTrue()); } protected SimpleRegistry(final Class type, final Predicate predicate) { final ImmutableMap.Builder builder = ImmutableMap.builder(); for (final T entry : type.getEnumConstants()) { if (predicate.test(entry)) { builder.put(entry.getKey(), entry); } } this.map = builder.build(); this.type = type; } @Override public @Nullable T get(final NamespacedKey key) { return this.map.get(key); } @Override public int size() { return map.size(); } @Override public Iterator iterator() { return this.map.values().iterator(); } @Override public Stream keyStream() { return this.map.keySet().stream(); } @ApiStatus.Internal @Deprecated(since = "1.20.6", forRemoval = true) public Class getType() { return this.type; } } @ApiStatus.Internal abstract class NotARegistry implements Registry { @Override public Stream stream() { return StreamSupport.stream(this.spliterator(), false); } @Override public Stream keyStream() { return stream().map(this::getKey); } @Override public int size() { return Iterables.size(this); } @Override public NamespacedKey getKey(final A value) { return value.getKey(); } @Override public boolean hasTag(final TagKey key) { throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); } @Override public Tag getTag(final TagKey key) { throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); } @Override public Collection> getTags() { throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); } } }