3
0
Mirror von https://github.com/PaperMC/Paper.git synchronisiert 2024-11-15 04:20:04 +01:00
Paper/patches/api/0471-Registry-Modification-API.patch
Nassim Jahnke 52a05907c7
Updated Upstream (Bukkit/CraftBukkit) (#11543)
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:
97c59261 PR-1073: Make Biome an interface
a38581aa Fix further javadoc errors
8271c490 Fix javadoc error
8a9ecf29 PR-1072: Fix bad naming for Vault State methods
6dd58108 PR-1071: Make Fluid an interface and add missing entry
ed2cdfc3 PR-1070: Make Attribute an interface and align names with the new minecraft ones
63472efb PR-1069: Add missing winter drop experimental annotation to pale boats

CraftBukkit Changes:
7235ad7b0 PR-1501: Make Biome an interface
602904003 PR-1500: Rename implementation for Vault State methods
75f26f79f PR-1499: Make Fluid an interface and add missing entry
4cfd87adc PR-1498: Make Attribute an interface and align names with the new minecraft ones
6bb0db5cb SPIGOT-7928: ExactChoice acts as MaterialChoice
3eaf3a13c SPIGOT-7929: Error when setting EquippableComponent
abbf57bac SPIGOT-7930: Fix spawning entities with SummonEntityEffect
92d6ab6cf PR-1497: Move boat field rename entries to below key renaming, so that keys are also renamed
abfe292aa PR-1496: Use correct Fluid class on Tags type check
c7aab7fa7 SPIGOT-7923: Fix Dispenser logic to avoid firing empty projectiles
2024-10-31 23:44:34 +01:00

913 Zeilen
34 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
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 <T> registry value type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryBuilder<T> {
+}
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 <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);
+}
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 <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();
+}
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.
+ * <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();
+}
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<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);
+ }
+
+}
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<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);
+}
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 <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);
+}
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 <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);
+}
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 <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();
+}
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 <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>> {
+}
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<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();
+ }
+}
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<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);
+ }
+}
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.
+ * <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;
+ }
+}
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 <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();
+ }
+}
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<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));
+ }
+}
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 <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();
+}
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<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();
+}
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<T>(RegistryKey<T> registryKey, Key key) implements TagKey<T> {
+
+ @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 e4d4ac6436f341f5d9de95e1ab56461fd68a3dc2..b56e8fc3fba40637396abef27c08806f5157b3b4 100644
--- a/src/main/java/org/bukkit/Registry.java
+++ b/src/main/java/org/bukkit/Registry.java
@@ -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.
*
@@ -561,5 +610,23 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
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<T> key) {
+ return Bukkit.getUnsafe().getTag(key) != null;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public io.papermc.paper.registry.tag.@NotNull Tag<T> getTag(final io.papermc.paper.registry.tag.@NotNull TagKey<T> key) {
+ final io.papermc.paper.registry.tag.Tag<T> 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 b503b5e13c51580367d53939ad4c19a7718c22ce..5b13617e497e847ef66214f9140aea0cd41f4c4f 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -281,4 +281,6 @@ public interface UnsafeValues {
// Paper end - lifecycle event API
@NotNull java.util.List<net.kyori.adventure.text.Component> computeTooltipLines(@NotNull ItemStack itemStack, @NotNull io.papermc.paper.inventory.tooltip.TooltipContext tooltipContext, @Nullable org.bukkit.entity.Player player); // Paper - expose itemstack tooltip lines
+
+ <A extends Keyed, M> io.papermc.paper.registry.tag.@Nullable Tag<A> getTag(io.papermc.paper.registry.tag.@NotNull TagKey<A> tagKey); // Paper - hack to get tags for non-server backed registries
}