3
0
Mirror von https://github.com/PaperMC/Paper.git synchronisiert 2024-11-15 04:20:04 +01:00
Paper/patches/api/0475-Registry-Modification-API.patch
Bjarne Koll 77a5779e24
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#11197)
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:
2ec53f49 PR-1050: Fix empty result check for Complex Recipes
10671012 PR-1044: Add CrafterCraftEvent
4d87ffe0 Use correct method in JavaDoc
ae5e5817 SPIGOT-7850: Add API for Bogged shear state
46b6d445 SPIGOT-7837: Support data pack banner patterns
d5d0cefc Fix JavaDoc error
b3c2b83d PR-1036: Add API for InventoryView derivatives
1fe2c75a SPIGOT-7809: Add ShieldMeta

CraftBukkit Changes:
8ee6fd1b8 SPIGOT-7857: Improve ItemMeta block data deserialization
8f26c30c6 SPIGOT-7857: Fix spurious internal NBT tag when deserializing BlockStateMeta
759061b93 SPIGOT-7855: Fire does not spread or burn blocks
00fc9fb64 SPIGOT-7853: AnvilInventory#getRepairCost() always returns 0
7501e2e04 PR-1450: Add CrafterCraftEvent
8c51673e7 SPIGOT-5731: PortalCreateEvent#getEntity returns null for nether portals ignited by flint and steel
d53d0d0b1 PR-1456: Fix inverted logic in CraftCrafterView#setSlotDisabled
682a678c8 SPIGOT-7850: Add API for Bogged shear state
fccf5243a SPIGOT-7837: Support data pack banner patterns
9c3bd4390 PR-1431: Add API for InventoryView derivatives
0cc6acbc4 SPIGOT-7849: Fix FoodComponent serialize with "using-converts-to" using null
2c5474952 Don't rely on tags for CraftItemMetas
20d107e46 SPIGOT-7846: Fix ItemMeta for hanging signs
76f59e315 Remove redundant clone in Dropper InventoryMoveItemEvent
e61a53d25 SPIGOT-7817: Call InventoryMoveItemEvent for Crafters
894682e2d SPIGOT-7839: Remove redundant Java version checks
2c12b2187 SPIGOT-7809: Add ShieldMeta and fix setting shield base colours

Spigot Changes:
fb8fb722 Rebuild patches
34bd42b7 SPIGOT-7835: Fix issue with custom hopper settings
2024-08-09 22:05:50 +02:00

894 Zeilen
35 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..6edf300c1d81c1001756141c9efd022ba0e372cd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java
@@ -0,0 +1,13 @@
+package io.papermc.paper.registry;
+
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * To be implemented by any type used for modifying registries.
+ *
+ * @param <T> registry value type
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+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..a5d7385eae9dfb88b52aed0e42c09a10ef807385
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java
@@ -0,0 +1,47 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * 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
+@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
+ */
+ @NonNull B builder();
+
+ /**
+ * Gets the key for this entry in the registry.
+ *
+ * @return the key
+ */
+ @NonNull 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
+ */
+ @NonNull <V extends Keyed> Tag<V> getOrCreateTag(@NonNull 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..498c5920926158e86c2aec2bd2129d5e8b8613a3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java
@@ -0,0 +1,23 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.registry.RegistryKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Base type for all registry events.
+ *
+ * @param <T> registry entry type
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface RegistryEvent<T> extends LifecycleEvent {
+
+ /**
+ * Get the key for the registry this event pertains to.
+ *
+ * @return the registry key
+ */
+ @NonNull 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..477ed0fd5acc923d429980529876f0dd7dd3a52a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java
@@ -0,0 +1,58 @@
+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.handler.configuration.LifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * 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
+@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
+ */
+ @NonNull 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.@NonNull Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> freeze();
+
+ /**
+ * Gets the registry key associated with this event type provider.
+ *
+ * @return the registry key
+ */
+ @NonNull 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..cfe47c8bd0888db6d00867acfefc8db42ef314aa
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java
@@ -0,0 +1,30 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+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..1f89945be2ed68f52a544f41f7a151b8fdfe113e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
@@ -0,0 +1,14 @@
+package io.papermc.paper.registry.event;
+
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryFreezeEvent}
+ * handlers for each applicable registry.
+ */
+@ApiStatus.Experimental
+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..12ec7e794a5047a30354a485acd40fa0f3438eea
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java
@@ -0,0 +1,39 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * 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
+@ApiStatus.NonExtendable
+public interface RegistryFreezeEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
+
+ /**
+ * Get the writable registry.
+ *
+ * @return a writable registry
+ */
+ @NonNull 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
+ */
+ @NonNull <V extends Keyed> Tag<V> getOrCreateTag(@NonNull 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..6de377275097f065c38dd59c6db9704018ac81fc
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java
@@ -0,0 +1,27 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * A registry which supports registering new objects.
+ *
+ * @param <T> registry entry type
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+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(@NonNull TypedKey<T> key, @NonNull 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..c5b484ee5f4a0f283a8d46f5490afac4b58b06be
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java
@@ -0,0 +1,42 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.Contract;
+
+/**
+ * Specific configuration for {@link io.papermc.paper.registry.event.RegistryEntryAddEvent}s.
+ *
+ * @param <T> registry entry type
+ */
+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 @NonNull RegistryEntryAddConfiguration<T> filter(final @NonNull 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")
+ @NonNull RegistryEntryAddConfiguration<T> filter(@NonNull Predicate<TypedKey<T>> filter);
+
+ @Override
+ @NonNull RegistryEntryAddConfiguration<T> priority(int priority);
+
+ @Override
+ @NonNull 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..f4d4ebf6cbed1b4a9955ceb2d0586782181d97e5
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java
@@ -0,0 +1,18 @@
+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;
+
+/**
+ * Lifecycle event type for {@link RegistryEntryAddEvent}s.
+ *
+ * @param <T> registry entry type
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.Experimental
+@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..b891101b43148f63c96b7dd611914c85d7b29dbf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java
@@ -0,0 +1,50 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+
+@ApiStatus.Experimental
+@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
+ */
+ @NonNull @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()
+ */
+ @NonNull @Unmodifiable Collection<T> resolve(final @NonNull 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(@NonNull TypedKey<T> valueKey);
+
+ @Override
+ default @NonNull 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..c712181ad4c6a9d00bc04f8a48515a388c692f48
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java
@@ -0,0 +1,53 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Nullable;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+record RegistryKeySetImpl<T extends 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 @Nullable 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 @Nullable 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..108df480e16131781a52c7bbba2ca6581e4c1ca1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistrySet.java
@@ -0,0 +1,112 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+
+/**
+ * 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
+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> @NonNull RegistryValueSet<T> valueSet(final @NonNull RegistryKey<T> registryKey, final @NonNull 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> @NonNull RegistryKeySet<T> keySetFromValues(final @NonNull RegistryKey<T> registryKey, final @NonNull 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 @NonNull RegistryKey<T> registryKey, final @NonNull TypedKey<T> @NonNull... 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> @NonNull RegistryKeySet<T> keySet(final @NonNull RegistryKey<T> registryKey, final @NonNull 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
+ */
+ @NonNull 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..58e2e42737d48b243854466eb7f7d3a844a86b6e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java
@@ -0,0 +1,34 @@
+package io.papermc.paper.registry.set;
+
+import java.util.Collection;
+import java.util.Iterator;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+
+/**
+ * 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
+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
+ */
+ @NonNull @Unmodifiable Collection<T> values();
+
+ @Override
+ default @NonNull 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..4ce5b26a1fcaae7b28ac8ed3c25014b66c266318
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java
@@ -0,0 +1,18 @@
+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;
+
+@ApiStatus.Internal
+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..ae374f68ef9baa16ed90c371f1622de0c0159873
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/tag/Tag.java
@@ -0,0 +1,25 @@
+package io.papermc.paper.registry.tag;
+
+import io.papermc.paper.registry.set.RegistryKeySet;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * 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
+public interface Tag<T extends Keyed> extends RegistryKeySet<T> { // TODO remove Keyed
+
+ /**
+ * Get the identifier for this named set.
+ *
+ * @return the tag key identifier
+ */
+ @NonNull 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..a49d328e95f7fda6567ee6c4f5f1878a2c187277
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/tag/TagKey.java
@@ -0,0 +1,32 @@
+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.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+
+@ApiStatus.Experimental
+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> @NonNull TagKey<T> create(final @NonNull RegistryKey<T> registryKey, final @NonNull Key key) {
+ return new TagKeyImpl<>(registryKey, key);
+ }
+
+ /**
+ * Get the registry key for this tag key.
+ *
+ * @return the registry key
+ */
+ @NonNull 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..11d19e339c7c62f2eb4467277552c27e4e83069c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java
@@ -0,0 +1,12 @@
+package io.papermc.paper.registry.tag;
+
+import io.papermc.paper.registry.RegistryKey;
+import net.kyori.adventure.key.Key;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+record TagKeyImpl<T>(RegistryKey<T> registryKey, Key key) implements TagKey<T> {
+}
diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java
index cd1e38debbec745dd13cd895327f544dcf42594d..c0428edee6ecbbd646c0e58da39a7ca476914fd1 100644
--- a/src/main/java/org/bukkit/Registry.java
+++ b/src/main/java/org/bukkit/Registry.java
@@ -358,6 +358,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
/**
@@ -432,6 +453,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
+
/**
* Returns a new stream, which contains all registry items, which are registered to the registry.
*
@@ -513,5 +562,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 31217b38e769f97801fa1afefeb223d1c755cabd..9bdba60fa96edbc4be5dcf54a815579db887048b 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -283,4 +283,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
}