From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Wed, 2 Mar 2022 13:36:21 -0800 Subject: [PATCH] Registry Modification API diff --git a/src/main/java/io/papermc/paper/registry/RegistryBuilder.java b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..379e2638aca243bacac777cf59982f9d0e601f3e --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java @@ -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 registry value type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryBuilder { +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..56468b311e40a6d1aa03c6d31328952b92e95027 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java @@ -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 registry entry type + * @param registry entry builder type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryEntryAddEvent> extends RegistryEvent { + + /** + * 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 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 the tag value type + */ + Tag getOrCreateTag(TagKey tagKey); +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..3a8643c06d751bf3e4bff4d95b62c675ecdf0f16 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java @@ -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 registry entry type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryEvent extends LifecycleEvent { + + /** + * Get the key for the registry this event pertains to. + * + * @return the registry key + */ + RegistryKey registryKey(); +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..8518e93829318cbfe0eb70f558cb86a7b5742514 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java @@ -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. + *

+ * Supported events are: + *

    + *
  • {@link RegistryEntryAddEvent} (via {@link #entryAdd()})
  • + *
  • {@link RegistryFreezeEvent} (via {@link #freeze()})
  • + *
+ * + * @param registry entry type + * @param registry entry builder type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryEventProvider> { + + /** + * Gets the event type for {@link RegistryEntryAddEvent} which is fired just before + * an object is added to a registry. + *

+ * 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 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. + *

+ * 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> freeze(); + + /** + * Gets the registry key associated with this event type provider. + * + * @return the registry key + */ + RegistryKey registryKey(); +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8d9afd49077090d30f13b217b71100c73137d120 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java @@ -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>(RegistryKey registryKey) implements RegistryEventProvider { + + static > RegistryEventProvider create(final RegistryKey registryKey) { + return new RegistryEventProviderImpl<>(registryKey); + } + + @Override + public RegistryEntryAddEventType entryAdd() { + return RegistryEventTypeProvider.provider().registryEntryAdd(this); + } + + @Override + public LifecycleEventType.Prioritizable> freeze() { + return RegistryEventTypeProvider.provider().registryFreeze(this); + } + +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProvider.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..d807bd2f42c98e37a96cf110ad77820dfffc8398 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProvider.java @@ -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 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()))); + } + + > RegistryEntryAddEventType registryEntryAdd(RegistryEventProvider type); + + > LifecycleEventType.Prioritizable> registryFreeze(RegistryEventProvider type); +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java new file mode 100644 index 0000000000000000000000000000000000000000..91ae9c0d3ec55ce417d4b447bf3d1b0d0c174b5e --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -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() { + } +} diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..59e8ca6c5b7fa0424ad9b2c74545ec53444b5fcb --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java @@ -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 registry entry type + * @param registry entry builder type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryFreezeEvent> extends RegistryEvent { + + /** + * Get the writable registry. + * + * @return a writable registry + */ + WritableRegistry 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 the tag value type + */ + Tag getOrCreateTag(TagKey tagKey); +} diff --git a/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..744f455b14cdc9131497024088da4ca0e8fc39dc --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java @@ -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 registry entry type + * @param registry entry builder type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface WritableRegistry> { + + /** + * 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 key, Consumer value); +} diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..d9bde790f74982c6cd5870aee35fec8a0c49fc83 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java @@ -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 registry entry type + */ +@NullMarked +public interface RegistryEntryAddConfiguration extends PrioritizedLifecycleEventHandlerConfiguration { + + /** + * 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 filter(final TypedKey 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 filter(Predicate> filter); + + @Override + RegistryEntryAddConfiguration priority(int priority); + + @Override + RegistryEntryAddConfiguration monitor(); +} diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java new file mode 100644 index 0000000000000000000000000000000000000000..93447ef58933f37fe12c4507c9a0b233be4c6c0b --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java @@ -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 registry entry type + * @param registry entry builder type + */ +@ApiStatus.Experimental +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryEntryAddEventType> extends LifecycleEventType, RegistryEntryAddConfiguration> { +} diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java b/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java new file mode 100644 index 0000000000000000000000000000000000000000..d5a5e332abc861d8509a5df7b57319f72e6b5449 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java @@ -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 extends Iterable>, RegistrySet { // 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> 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 resolve(final Registry 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 valueKey); + + @Override + default Iterator> iterator() { + return this.values().iterator(); + } +} diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java b/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..fe4a0817d7ac4a9f958a6156d6ae3ac1c96f58cd --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java @@ -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(RegistryKey registryKey, List> values) implements RegistryKeySet { // TODO remove Keyed + + static RegistryKeySet create(final RegistryKey registryKey, final Iterable values) { // TODO remove Keyed + final Registry registry = RegistryAccess.registryAccess().getRegistry(registryKey); + final ArrayList> 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 valueKey) { + return this.values.contains(valueKey); + } + + @Override + public Collection resolve(final Registry registry) { + final List values = new ArrayList<>(this.values.size()); + for (final TypedKey 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); + } +} diff --git a/src/main/java/io/papermc/paper/registry/set/RegistrySet.java b/src/main/java/io/papermc/paper/registry/set/RegistrySet.java new file mode 100644 index 0000000000000000000000000000000000000000..c6fdeb05242418d20472aeed8cb61bfe937911c8 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/set/RegistrySet.java @@ -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. + *

+ * There are 2 types of registry sets: + *

    + *
  • {@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)}.
  • + *
  • {@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)}.
  • + * + *
+ * + * @param registry value type + */ +@ApiStatus.Experimental +@NullMarked +public sealed interface RegistrySet permits RegistryKeySet, RegistryValueSet { + + // TODO uncomment when direct holder sets need to be exposed to the API + // /** + // * Creates a {@link RegistryValueSet} from anonymous values. + // *

All values provided must not have keys in the given registry.

+ // * + // * @param registryKey the registry key for the type of these values + // * @param values the values + // * @return a new registry set + // * @param the type of the values + // */ + // @Contract(value = "_, _ -> new", pure = true) + // static RegistryValueSet valueSet(final RegistryKey registryKey, final Iterable values) { + // return RegistryValueSetImpl.create(registryKey, values); + // } + + /** + * Creates a {@link RegistryKeySet} from registry-backed values. + *

All values provided must have keys in the given registry. + *

+ *

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}.

+ * + * @param registryKey the registry key for the owner of these values + * @param values the values + * @return a new registry set + * @param 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 RegistryKeySet keySetFromValues(final RegistryKey registryKey, final Iterable 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 the type of the values + */ + @SafeVarargs + static RegistryKeySet keySet(final RegistryKey registryKey, final TypedKey... 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 the type of the values + */ + @SuppressWarnings("BoundedWildcard") + @Contract(value = "_, _ -> new", pure = true) + static RegistryKeySet keySet(final RegistryKey registryKey, final Iterable> keys) { // TODO remove Keyed + return new RegistryKeySetImpl<>(registryKey, Lists.newArrayList(keys)); + } + + /** + * Get the registry key for this set. + * + * @return the registry key + */ + RegistryKey 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; + } +} diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java b/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java new file mode 100644 index 0000000000000000000000000000000000000000..9f7761535c30c376dad64678fa2ee24bbb041e06 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java @@ -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 registry value type + */ +@ApiStatus.Experimental +@NullMarked +public sealed interface RegistryValueSet extends Iterable, RegistrySet permits RegistryValueSetImpl { + + @Override + default int size() { + return this.values().size(); + } + + /** + * Get the collection of values in this direct set. + * + * @return the values + */ + @Unmodifiable Collection values(); + + @Override + default Iterator iterator() { + return this.values().iterator(); + } +} diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java b/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1bcc6b4b9ff0090570d4f39a49e10e1fa03edb7d --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java @@ -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(RegistryKey registryKey, List values) implements RegistryValueSet { + + RegistryValueSetImpl { + values = List.copyOf(values); + } + + static RegistryValueSet create(final RegistryKey registryKey, final Iterable values) { + return new RegistryValueSetImpl<>(registryKey, Lists.newArrayList(values)); + } +} diff --git a/src/main/java/io/papermc/paper/registry/tag/Tag.java b/src/main/java/io/papermc/paper/registry/tag/Tag.java new file mode 100644 index 0000000000000000000000000000000000000000..245eb1074ce2930e4d9ff42a5df49004d08bbac2 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/tag/Tag.java @@ -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 + * @see org.bukkit.Tag + * @see org.bukkit.Registry#getTag(TagKey) + */ +@ApiStatus.Experimental +@NullMarked +public interface Tag extends RegistryKeySet { // TODO remove Keyed + + /** + * Get the identifier for this named set. + * + * @return the tag key identifier + */ + TagKey tagKey(); +} diff --git a/src/main/java/io/papermc/paper/registry/tag/TagKey.java b/src/main/java/io/papermc/paper/registry/tag/TagKey.java new file mode 100644 index 0000000000000000000000000000000000000000..f84d95b29fd2c0a52d2769fa9d3e2326ebc8aa3f --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/tag/TagKey.java @@ -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 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 the registry value type + */ + @Contract(value = "_, _ -> new", pure = true) + static TagKey create(final RegistryKey registryKey, final Key key) { + return new TagKeyImpl<>(registryKey, key); + } + + /** + * Get the registry key for this tag key. + * + * @return the registry key + */ + RegistryKey registryKey(); +} diff --git a/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java b/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..bf49125acc8a0508bf59674bba3ed3505ee9481a --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java @@ -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(RegistryKey registryKey, Key key) implements TagKey { + + @Override + public String toString() { + return "#" + this.key + " (in " + this.registryKey + ")"; + } +} diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java index 67cf3fcad21a8977d6fad172cc776b628ab68f25..b4ef3133fdd9d79a3381cf8f659ff561ab2b4fad 100644 --- a/src/main/java/org/bukkit/Registry.java +++ b/src/main/java/org/bukkit/Registry.java @@ -378,6 +378,27 @@ public interface Registry extends Iterable { */ @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 typedKey) { + return this.get(typedKey.key()); + } + // Paper end // Paper start - improve Registry /** @@ -452,6 +473,34 @@ public interface Registry extends Iterable { } // 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 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 getTag(final io.papermc.paper.registry.tag.@NotNull TagKey key) { + throw new UnsupportedOperationException(this + " doesn't have tags"); + } + // Paper end - RegistrySet API + /** * Get the object by its key. * @@ -555,5 +604,23 @@ public interface Registry extends Iterable { return value.getKey(); } // Paper end - improve Registry + + // Paper start - RegistrySet API + @SuppressWarnings("deprecation") + @Override + public boolean hasTag(final io.papermc.paper.registry.tag.@NotNull TagKey key) { + return Bukkit.getUnsafe().getTag(key) != null; + } + + @SuppressWarnings("deprecation") + @Override + public io.papermc.paper.registry.tag.@NotNull Tag getTag(final io.papermc.paper.registry.tag.@NotNull TagKey key) { + final io.papermc.paper.registry.tag.Tag tag = Bukkit.getUnsafe().getTag(key); + if (tag == null) { + throw new java.util.NoSuchElementException("No tag " + key + " found"); + } + return tag; + } + // Paper end - RegistrySet API } } diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java index e5144471056e69586c1693a9264a3995387de3cc..2c365ecf3f5a5252e489bc1dc04359e766a2d739 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java @@ -272,4 +272,6 @@ public interface UnsafeValues { // Paper end - lifecycle event API @NotNull java.util.List computeTooltipLines(@NotNull ItemStack itemStack, @NotNull io.papermc.paper.inventory.tooltip.TooltipContext tooltipContext, @Nullable org.bukkit.entity.Player player); // Paper - expose itemstack tooltip lines + + io.papermc.paper.registry.tag.@Nullable Tag getTag(io.papermc.paper.registry.tag.@NotNull TagKey tagKey); // Paper - hack to get tags for non-server backed registries }