Registry Modification API

This commit is contained in:
Jake Potrebic 2022-03-02 13:36:21 -08:00
parent ada45eba8a
commit 146cd1a015
20 changed files with 739 additions and 0 deletions

View file

@ -0,0 +1,15 @@
package io.papermc.paper.registry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* To be implemented by any type used for modifying registries.
*
* @param <T> registry value type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface RegistryBuilder<T> {
}

View file

@ -0,0 +1,48 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Event object for {@link RegistryEventProvider#entryAdd()}. This
* event is fired right before a specific entry is registered in/added to registry.
* It provides a way for plugins to modify parts of this entry.
*
* @param <T> registry entry type
* @param <B> registry entry builder type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface RegistryEntryAddEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
/**
* Gets the builder for the entry being added to the registry.
*
* @return the object builder
*/
B builder();
/**
* Gets the key for this entry in the registry.
*
* @return the key
*/
TypedKey<T> key();
/**
* Gets or creates a tag for the given tag key. This tag
* is then required to be filled either from the built-in or
* custom datapack.
*
* @param tagKey the tag key
* @return the tag
* @param <V> the tag value type
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey);
}

View file

@ -0,0 +1,24 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
import io.papermc.paper.registry.RegistryKey;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Base type for all registry events.
*
* @param <T> registry entry type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface RegistryEvent<T> extends LifecycleEvent {
/**
* Get the key for the registry this event pertains to.
*
* @return the registry key
*/
RegistryKey<T> registryKey();
}

View file

@ -0,0 +1,57 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Provider for registry events for a specific registry.
* <p>
* Supported events are:
* <ul>
* <li>{@link RegistryEntryAddEvent} (via {@link #entryAdd()})</li>
* <li>{@link RegistryFreezeEvent} (via {@link #freeze()})</li>
* </ul>
*
* @param <T> registry entry type
* @param <B> registry entry builder type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface RegistryEventProvider<T, B extends RegistryBuilder<T>> {
/**
* Gets the event type for {@link RegistryEntryAddEvent} which is fired just before
* an object is added to a registry.
* <p>
* Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventType, LifecycleEventHandler)}
* to register a handler for {@link RegistryEntryAddEvent}.
*
* @return the registry entry add event type
*/
RegistryEntryAddEventType<T, B> entryAdd();
/**
* Gets the event type for {@link RegistryFreezeEvent} which is fired just before
* a registry is frozen. It allows for the registration of new objects.
* <p>
* Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventType, LifecycleEventHandler)}
* to register a handler for {@link RegistryFreezeEvent}.
*
* @return the registry freeze event type
*/
LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> freeze();
/**
* Gets the registry key associated with this event type provider.
*
* @return the registry key
*/
RegistryKey<T> registryKey();
}

View file

@ -0,0 +1,29 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Internal
@NullMarked
record RegistryEventProviderImpl<T, B extends RegistryBuilder<T>>(RegistryKey<T> registryKey) implements RegistryEventProvider<T, B> {
static <T, B extends RegistryBuilder<T>> RegistryEventProvider<T, B> create(final RegistryKey<T> registryKey) {
return new RegistryEventProviderImpl<>(registryKey);
}
@Override
public RegistryEntryAddEventType<T, B> entryAdd() {
return RegistryEventTypeProvider.provider().registryEntryAdd(this);
}
@Override
public LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> freeze() {
return RegistryEventTypeProvider.provider().registryFreeze(this);
}
}

View file

@ -0,0 +1,24 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import java.util.Optional;
import java.util.ServiceLoader;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
interface RegistryEventTypeProvider {
Optional<RegistryEventTypeProvider> PROVIDER = ServiceLoader.load(RegistryEventTypeProvider.class)
.findFirst();
static RegistryEventTypeProvider provider() {
return PROVIDER.orElseThrow(() -> new IllegalStateException("Could not find a %s service implementation".formatted(RegistryEventTypeProvider.class.getSimpleName())));
}
<T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> registryEntryAdd(RegistryEventProvider<T, B> type);
<T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> registryFreeze(RegistryEventProvider<T, B> type);
}

View file

@ -0,0 +1,16 @@
package io.papermc.paper.registry.event;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryFreezeEvent}
* handlers for each applicable registry.
*/
@ApiStatus.Experimental
@NullMarked
public final class RegistryEvents {
private RegistryEvents() {
}
}

View file

@ -0,0 +1,40 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Event object for {@link RegistryEventProvider#freeze()}. This
* event is fired right before a registry is frozen disallowing further changes.
* It provides a way for plugins to add new objects to the registry.
*
* @param <T> registry entry type
* @param <B> registry entry builder type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface RegistryFreezeEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
/**
* Get the writable registry.
*
* @return a writable registry
*/
WritableRegistry<T, B> registry();
/**
* Gets or creates a tag for the given tag key. This tag
* is then required to be filled either from the built-in or
* custom datapack.
*
* @param tagKey the tag key
* @return the tag
* @param <V> the tag value type
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey);
}

View file

@ -0,0 +1,28 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.TypedKey;
import java.util.function.Consumer;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* A registry which supports registering new objects.
*
* @param <T> registry entry type
* @param <B> registry entry builder type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface WritableRegistry<T, B extends RegistryBuilder<T>> {
/**
* Register a new value with the specified key. This will
* fire a {@link RegistryEntryAddEvent} for the new entry.
*
* @param key the entry's key (must be unique from others)
* @param value a consumer for the entry's builder
*/
void register(TypedKey<T> key, Consumer<? super B> value);
}

View file

@ -0,0 +1,43 @@
package io.papermc.paper.registry.event.type;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
import io.papermc.paper.registry.TypedKey;
import java.util.function.Predicate;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
/**
* Specific configuration for {@link io.papermc.paper.registry.event.RegistryEntryAddEvent}s.
*
* @param <T> registry entry type
*/
@NullMarked
public interface RegistryEntryAddConfiguration<T> extends PrioritizedLifecycleEventHandlerConfiguration<BootstrapContext> {
/**
* Only call the handler if the value being added matches the specified key.
*
* @param key the key to match
* @return this configuration
*/
@Contract(value = "_ -> this", mutates = "this")
default RegistryEntryAddConfiguration<T> filter(final TypedKey<T> key) {
return this.filter(key::equals);
}
/**
* Only call the handler if the value being added passes the provided filter.
*
* @param filter the predicate to match the key against
* @return this configuration
*/
@Contract(value = "_ -> this", mutates = "this")
RegistryEntryAddConfiguration<T> filter(Predicate<TypedKey<T>> filter);
@Override
RegistryEntryAddConfiguration<T> priority(int priority);
@Override
RegistryEntryAddConfiguration<T> monitor();
}

View file

@ -0,0 +1,20 @@
package io.papermc.paper.registry.event.type;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.event.RegistryEntryAddEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Lifecycle event type for {@link RegistryEntryAddEvent}s.
*
* @param <T> registry entry type
* @param <B> registry entry builder type
*/
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public interface RegistryEntryAddEventType<T, B extends RegistryBuilder<T>> extends LifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, RegistryEntryAddConfiguration<T>> {
}

View file

@ -0,0 +1,51 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.TypedKey;
import java.util.Collection;
import java.util.Iterator;
import org.bukkit.Keyed;
import org.bukkit.Registry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
public non-sealed interface RegistryKeySet<T extends Keyed> extends Iterable<TypedKey<T>>, RegistrySet<T> { // TODO remove Keyed
@Override
default int size() {
return this.values().size();
}
/**
* Get the keys for the values in this set.
*
* @return the keys
*/
@Unmodifiable Collection<TypedKey<T>> values();
/**
* Resolve this set into a collection of values. Prefer using
* {@link #values()}.
*
* @param registry the registry to resolve the values from (must match {@link #registryKey()})
* @return the resolved values
* @see RegistryKeySet#values()
*/
@Unmodifiable Collection<T> resolve(final Registry<T> registry);
/**
* Checks if this set contains the value with the given key.
*
* @param valueKey the key to check
* @return true if the value is in this set
*/
boolean contains(TypedKey<T> valueKey);
@Override
default Iterator<TypedKey<T>> iterator() {
return this.values().iterator();
}
}

View file

@ -0,0 +1,52 @@
package io.papermc.paper.registry.set;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@ApiStatus.Internal
@NullMarked
record RegistryKeySetImpl<T extends @Nullable Keyed>(RegistryKey<T> registryKey, List<TypedKey<T>> values) implements RegistryKeySet<T> { // TODO remove Keyed
static <T extends Keyed> RegistryKeySet<T> create(final RegistryKey<T> registryKey, final Iterable<? extends T> values) { // TODO remove Keyed
final Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
final ArrayList<TypedKey<T>> keys = new ArrayList<>();
for (final T value : values) {
final NamespacedKey key = registry.getKey(value);
Preconditions.checkArgument(key != null, value + " does not have a key in " + registryKey);
keys.add(TypedKey.create(registryKey, key));
}
return new RegistryKeySetImpl<>(registryKey, keys);
}
RegistryKeySetImpl {
values = List.copyOf(values);
}
@Override
public boolean contains(final TypedKey<T> valueKey) {
return this.values.contains(valueKey);
}
@Override
public Collection<T> resolve(final Registry<T> registry) {
final List<T> values = new ArrayList<>(this.values.size());
for (final TypedKey<T> key : this.values) {
final T value = registry.get(key.key());
Preconditions.checkState(value != null, "Trying to access unbound TypedKey: " + key);
values.add(value);
}
return Collections.unmodifiableList(values);
}
}

View file

@ -0,0 +1,113 @@
package io.papermc.paper.registry.set;
import com.google.common.collect.Lists;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
/**
* Represents a collection tied to a registry.
* <p>
* There are 2<!--3--> types of registry sets:
* <ul>
* <li>{@link Tag} which is a tag from vanilla or a datapack.
* These are obtained via {@link org.bukkit.Registry#getTag(io.papermc.paper.registry.tag.TagKey)}.</li>
* <li>{@link RegistryKeySet} which is a set of of keys linked to values that are present in the registry. These are
* created via {@link #keySet(RegistryKey, Iterable)} or {@link #keySetFromValues(RegistryKey, Iterable)}.</li>
* <!-- <li>{@link RegistryValueSet} which is a set of values which are anonymous (don't have keys in the registry). These are
* created via {@link #valueSet(RegistryKey, Iterable)}.</li>-->
* </ul>
*
* @param <T> registry value type
*/
@ApiStatus.Experimental
@NullMarked
public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet {
// TODO uncomment when direct holder sets need to be exposed to the API
// /**
// * Creates a {@link RegistryValueSet} from anonymous values.
// * <p>All values provided <b>must not</b> have keys in the given registry.</p>
// *
// * @param registryKey the registry key for the type of these values
// * @param values the values
// * @return a new registry set
// * @param <T> the type of the values
// */
// @Contract(value = "_, _ -> new", pure = true)
// static <T> RegistryValueSet<T> valueSet(final RegistryKey<T> registryKey, final Iterable<? extends T> values) {
// return RegistryValueSetImpl.create(registryKey, values);
// }
/**
* Creates a {@link RegistryKeySet} from registry-backed values.
* <p>All values provided <b>must</b> have keys in the given registry.
* <!--For anonymous values, use {@link #valueSet(RegistryKey, Iterable)}--></p>
* <p>If references to actual objects are not available yet, use {@link #keySet(RegistryKey, Iterable)} to
* create an equivalent {@link RegistryKeySet} using just {@link TypedKey TypedKeys}.</p>
*
* @param registryKey the registry key for the owner of these values
* @param values the values
* @return a new registry set
* @param <T> the type of the values
* @throws IllegalArgumentException if the registry isn't available yet or if any value doesn't have a key in that registry
*/
@Contract(value = "_, _ -> new", pure = true)
static <T extends Keyed> RegistryKeySet<T> keySetFromValues(final RegistryKey<T> registryKey, final Iterable<? extends T> values) { // TODO remove Keyed
return RegistryKeySetImpl.create(registryKey, values);
}
/**
* Creates a direct {@link RegistrySet} from {@link TypedKey TypedKeys}.
*
* @param registryKey the registry key for the owner of these keys
* @param keys the keys for the values
* @return a new registry set
* @param <T> the type of the values
*/
@SafeVarargs
static <T extends Keyed> RegistryKeySet<T> keySet(final RegistryKey<T> registryKey, final TypedKey<T>... keys) { // TODO remove Keyed
return keySet(registryKey, Lists.newArrayList(keys));
}
/**
* Creates a direct {@link RegistrySet} from {@link TypedKey TypedKeys}.
*
* @param registryKey the registry key for the owner of these keys
* @param keys the keys for the values
* @return a new registry set
* @param <T> the type of the values
*/
@SuppressWarnings("BoundedWildcard")
@Contract(value = "_, _ -> new", pure = true)
static <T extends Keyed> RegistryKeySet<T> keySet(final RegistryKey<T> registryKey, final Iterable<TypedKey<T>> keys) { // TODO remove Keyed
return new RegistryKeySetImpl<>(registryKey, Lists.newArrayList(keys));
}
/**
* Get the registry key for this set.
*
* @return the registry key
*/
RegistryKey<T> registryKey();
/**
* Get the size of this set.
*
* @return the size
*/
int size();
/**
* Checks if the registry set is empty.
*
* @return true, if empty
*/
default boolean isEmpty() {
return this.size() == 0;
}
}

View file

@ -0,0 +1,35 @@
package io.papermc.paper.registry.set;
import java.util.Collection;
import java.util.Iterator;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
/**
* A collection of anonymous values relating to a registry. These
* are values of the same type as the registry, but will not be found
* in the registry, hence, anonymous.
* @param <T> registry value type
*/
@ApiStatus.Experimental
@NullMarked
public sealed interface RegistryValueSet<T> extends Iterable<T>, RegistrySet<T> permits RegistryValueSetImpl {
@Override
default int size() {
return this.values().size();
}
/**
* Get the collection of values in this direct set.
*
* @return the values
*/
@Unmodifiable Collection<T> values();
@Override
default Iterator<T> iterator() {
return this.values().iterator();
}
}

View file

@ -0,0 +1,20 @@
package io.papermc.paper.registry.set;
import com.google.common.collect.Lists;
import io.papermc.paper.registry.RegistryKey;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Internal
@NullMarked
record RegistryValueSetImpl<T>(RegistryKey<T> registryKey, List<T> values) implements RegistryValueSet<T> {
RegistryValueSetImpl {
values = List.copyOf(values);
}
static <T> RegistryValueSet<T> create(final RegistryKey<T> registryKey, final Iterable<? extends T> values) {
return new RegistryValueSetImpl<>(registryKey, Lists.newArrayList(values));
}
}

View file

@ -0,0 +1,26 @@
package io.papermc.paper.registry.tag;
import io.papermc.paper.registry.set.RegistryKeySet;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* A named {@link RegistryKeySet} which are created
* via the datapack tag system.
*
* @param <T>
* @see org.bukkit.Tag
* @see org.bukkit.Registry#getTag(TagKey)
*/
@ApiStatus.Experimental
@NullMarked
public interface Tag<T extends Keyed> extends RegistryKeySet<T> { // TODO remove Keyed
/**
* Get the identifier for this named set.
*
* @return the tag key identifier
*/
TagKey<T> tagKey();
}

View file

@ -0,0 +1,33 @@
package io.papermc.paper.registry.tag;
import io.papermc.paper.registry.RegistryKey;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Experimental
@NullMarked
public sealed interface TagKey<T> extends Keyed permits TagKeyImpl {
/**
* Creates a new tag key for a registry.
*
* @param registryKey the registry for the tag
* @param key the specific key for the tag
* @return a new tag key
* @param <T> the registry value type
*/
@Contract(value = "_, _ -> new", pure = true)
static <T> TagKey<T> create(final RegistryKey<T> registryKey, final Key key) {
return new TagKeyImpl<>(registryKey, key);
}
/**
* Get the registry key for this tag key.
*
* @return the registry key
*/
RegistryKey<T> registryKey();
}

View file

@ -0,0 +1,16 @@
package io.papermc.paper.registry.tag;
import io.papermc.paper.registry.RegistryKey;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@ApiStatus.Internal
@NullMarked
record TagKeyImpl<T>(RegistryKey<T> registryKey, Key key) implements TagKey<T> {
@Override
public String toString() {
return "#" + this.key + " (in " + this.registryKey + ")";
}
}

View file

@ -384,6 +384,27 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
*/
@Nullable
T get(@NotNull 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 net.kyori.adventure.key.@NotNull 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 io.papermc.paper.registry.@NotNull TypedKey<T> typedKey) {
return this.get(typedKey.key());
}
// Paper end
// Paper start - improve Registry
/**
@ -458,6 +479,34 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
}
// 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
* @see #getTag(io.papermc.paper.registry.tag.TagKey)
*/
@ApiStatus.Experimental
default boolean hasTag(final io.papermc.paper.registry.tag.@NotNull TagKey<T> key) {
throw new UnsupportedOperationException(this + " doesn't have tags");
}
/**
* 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 java.util.NoSuchElementException if no tag with the given key is found
* @throws UnsupportedOperationException if this registry doesn't have or support tags
* @see #hasTag(io.papermc.paper.registry.tag.TagKey)
*/
@ApiStatus.Experimental
default @NotNull io.papermc.paper.registry.tag.Tag<T> getTag(final io.papermc.paper.registry.tag.@NotNull TagKey<T> key) {
throw new UnsupportedOperationException(this + " doesn't have tags");
}
// Paper end - RegistrySet API
/**
* Get the object by its key.
*