3
0
Mirror von https://github.com/PaperMC/Paper.git synchronisiert 2024-12-15 02:50:09 +01:00
Paper/patches/server/0993-Registry-Modification-API.patch
Jake Potrebic c6aa61ee18
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#11561)
Updated Upstream (Bukkit/CraftBukkit/Spigot)

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:
b9df8e9f SPIGOT-7933: Improve custom Minecart max speed
fc496179 Fix InstrumentTest
7c0ec598 PR-1075: Make Art an interface
c389f5a4 PR-1074: Make Sound an interface

CraftBukkit Changes:
df1efc0bb SPIGOT-7945: `Bukkit#dispatchCommand` throws `UnsupportedOperationException`
285df6e85 SPIGOT-7933: Improve custom Minecart max speed
a0f3d4e50 SPIGOT-7940: Recipe book errors after reload
9e0618ec2 SPIGOT-7937: Cannot spawn minecart during world generation with minecart_improvements enabled
1eb4d28da SPIGOT-7941: Fix resistance over 4 amplify causing issues in damage
52b99158a PR-1504: Make Art an interface
e18ae35f1 PR-1502: Make Sound an interface

Spigot Changes:
e65d67a7 SPIGOT-7934: Item entities start "bouncing" under certain conditions
2024-11-04 18:42:38 +01:00

1500 Zeilen
78 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 27 Feb 2023 18:28:39 -0800
Subject: [PATCH] Registry Modification API
== AT ==
public net.minecraft.core.MappedRegistry validateWrite(Lnet/minecraft/resources/ResourceKey;)V
public net.minecraft.resources.RegistryOps lookupProvider
public net.minecraft.resources.RegistryOps$HolderLookupAdapter
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
index 39120c63ed25d45a4083523fbfe79d871f4d892e..f563e6e7a558d22f571154640e99cc86718c89f5 100644
--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
@@ -2,6 +2,7 @@ package io.papermc.paper.registry;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.registry.entry.RegistryEntry;
+import io.papermc.paper.registry.tag.TagKey;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -68,6 +69,7 @@ import org.checkerframework.framework.qual.DefaultQualifier;
import static io.papermc.paper.registry.entry.RegistryEntry.apiOnly;
import static io.papermc.paper.registry.entry.RegistryEntry.entry;
+import static io.papermc.paper.registry.entry.RegistryEntry.writable;
@DefaultQualifier(NonNull.class)
public final class PaperRegistries {
@@ -151,6 +153,15 @@ public final class PaperRegistries {
return ResourceKey.create((ResourceKey<? extends Registry<M>>) PaperRegistries.registryToNms(typedKey.registryKey()), PaperAdventure.asVanilla(typedKey.key()));
}
+ public static <M, T> TagKey<T> fromNms(final net.minecraft.tags.TagKey<M> tagKey) {
+ return TagKey.create(registryFromNms(tagKey.registry()), CraftNamespacedKey.fromMinecraft(tagKey.location()));
+ }
+
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ public static <M, T> net.minecraft.tags.TagKey<M> toNms(final TagKey<T> tagKey) {
+ return net.minecraft.tags.TagKey.create((ResourceKey<? extends Registry<M>>) registryToNms(tagKey.registryKey()), PaperAdventure.asVanilla(tagKey.key()));
+ }
+
private PaperRegistries() {
}
}
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
index 35b6a7c5bac9640332a833bd3627f2bcb1bbf2f3..1026b9c04f94ed73049b980822a2eafdbacea7fd 100644
--- a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
@@ -83,6 +83,14 @@ public class PaperRegistryAccess implements RegistryAccess {
return possiblyUnwrap(registryHolder.get());
}
+ public <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> WritableCraftRegistry<M, T, B> getWritableRegistry(final RegistryKey<T> key) {
+ final Registry<T> registry = this.getRegistry(key);
+ if (registry instanceof WritableCraftRegistry<?, T, ?>) {
+ return (WritableCraftRegistry<M, T, B>) registry;
+ }
+ throw new IllegalArgumentException(key + " does not point to a writable registry");
+ }
+
private static <T extends Keyed> Registry<T> possiblyUnwrap(final Registry<T> registry) {
if (registry instanceof final DelayedRegistry<T, ?> delayedRegistry) { // if not coming from legacy, unwrap the delayed registry
return delayedRegistry.delegate();
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..528c6ee1739d92f766f3904acd7fc5734c93388a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java
@@ -0,0 +1,26 @@
+package io.papermc.paper.registry;
+
+import io.papermc.paper.registry.data.util.Conversions;
+import net.minecraft.resources.RegistryOps;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public interface PaperRegistryBuilder<M, T> extends RegistryBuilder<T> {
+
+ M build();
+
+ @FunctionalInterface
+ interface Filler<M, T, B extends PaperRegistryBuilder<M, T>> {
+
+ B fill(Conversions conversions, TypedKey<T> key, @Nullable M nms);
+
+ default Factory<M, T, B> asFactory() {
+ return (lookup, key) -> this.fill(lookup, key, null);
+ }
+ }
+
+ @FunctionalInterface
+ interface Factory<M, T, B extends PaperRegistryBuilder<M, T>> {
+
+ B create(Conversions conversions, TypedKey<T> key);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..69e946173407eb05b18a2b19b0d45cbb3213570b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
@@ -0,0 +1,183 @@
+package io.papermc.paper.registry;
+
+import com.google.common.base.Preconditions;
+import com.mojang.serialization.Lifecycle;
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.entrypoint.Entrypoint;
+import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
+import io.papermc.paper.registry.data.util.Conversions;
+import io.papermc.paper.registry.entry.RegistryEntry;
+import io.papermc.paper.registry.entry.RegistryEntryInfo;
+import io.papermc.paper.registry.event.RegistryEntryAddEventImpl;
+import io.papermc.paper.registry.event.RegistryEventMap;
+import io.papermc.paper.registry.event.RegistryEventProvider;
+import io.papermc.paper.registry.event.RegistryFreezeEvent;
+import io.papermc.paper.registry.event.RegistryFreezeEventImpl;
+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
+import io.papermc.paper.registry.event.type.RegistryEntryAddEventTypeImpl;
+import io.papermc.paper.registry.event.type.RegistryLifecycleEventType;
+import java.util.Optional;
+import net.kyori.adventure.key.Key;
+import net.minecraft.core.Holder;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.RegistrationInfo;
+import net.minecraft.core.Registry;
+import net.minecraft.core.WritableRegistry;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.intellij.lang.annotations.Subst;
+
+public class PaperRegistryListenerManager {
+
+ public static final PaperRegistryListenerManager INSTANCE = new PaperRegistryListenerManager();
+
+ public final RegistryEventMap valueAddEventTypes = new RegistryEventMap("value add");
+ public final RegistryEventMap freezeEventTypes = new RegistryEventMap("freeze");
+
+ private PaperRegistryListenerManager() {
+ }
+
+ /**
+ * For {@link Registry#register(Registry, String, Object)}
+ */
+ public <M> M registerWithListeners(final Registry<M> registry, final String id, final M nms) {
+ return this.registerWithListeners(registry, ResourceLocation.withDefaultNamespace(id), nms);
+ }
+
+ /**
+ * For {@link Registry#register(Registry, ResourceLocation, Object)}
+ */
+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
+ return this.registerWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
+ }
+
+ /**
+ * For {@link Registry#register(Registry, ResourceKey, Object)}
+ */
+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
+ return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, PaperRegistryListenerManager::registerWithInstance, BuiltInRegistries.BUILT_IN_CONVERSIONS);
+ }
+
+ /**
+ * For {@link Registry#registerForHolder(Registry, ResourceLocation, Object)}
+ */
+ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
+ return this.registerForHolderWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
+ }
+
+ /**
+ * For {@link Registry#registerForHolder(Registry, ResourceKey, Object)}
+ */
+ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
+ return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, WritableRegistry::register, BuiltInRegistries.BUILT_IN_CONVERSIONS);
+ }
+
+ public <M> void registerWithListeners(
+ final Registry<M> registry,
+ final ResourceKey<M> key,
+ final M nms,
+ final RegistrationInfo registrationInfo,
+ final Conversions conversions
+ ) {
+ this.registerWithListeners(registry, key, nms, registrationInfo, WritableRegistry::register, conversions);
+ }
+
+ // TODO remove Keyed
+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>, R> R registerWithListeners(
+ final Registry<M> registry,
+ final ResourceKey<M> key,
+ final M nms,
+ final RegistrationInfo registrationInfo,
+ final RegisterMethod<M, R> registerMethod,
+ final Conversions conversions
+ ) {
+ Preconditions.checkState(LaunchEntryPointHandler.INSTANCE.hasEntered(Entrypoint.BOOTSTRAPPER), registry.key() + " tried to run modification listeners before bootstrappers have been called"); // verify that bootstrappers have been called
+ final @Nullable RegistryEntryInfo<M, T> entry = PaperRegistries.getEntry(registry.key());
+ if (!RegistryEntry.Modifiable.isModifiable(entry) || !this.valueAddEventTypes.hasHandlers(entry.apiKey())) {
+ return registerMethod.register((WritableRegistry<M>) registry, key, nms, registrationInfo);
+ }
+ final RegistryEntry.Modifiable<M, T, B> modifiableEntry = RegistryEntry.Modifiable.asModifiable(entry);
+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.apiKey(), Key.key(key.location().getNamespace(), key.location().getPath()));
+ final B builder = modifiableEntry.fillBuilder(conversions, typedKey, nms);
+ return this.registerWithListeners(registry, modifiableEntry, key, nms, builder, registrationInfo, registerMethod, conversions);
+ }
+
+ <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void registerWithListeners( // TODO remove Keyed
+ final WritableRegistry<M> registry,
+ final RegistryEntryInfo<M, T> entry,
+ final ResourceKey<M> key,
+ final B builder,
+ final RegistrationInfo registrationInfo,
+ final Conversions conversions
+ ) {
+ if (!RegistryEntry.Modifiable.isModifiable(entry) || !this.valueAddEventTypes.hasHandlers(entry.apiKey())) {
+ registry.register(key, builder.build(), registrationInfo);
+ return;
+ }
+ this.registerWithListeners(registry, RegistryEntry.Modifiable.asModifiable(entry), key, null, builder, registrationInfo, WritableRegistry::register, conversions);
+ }
+
+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>, R> R registerWithListeners( // TODO remove Keyed
+ final Registry<M> registry,
+ final RegistryEntry.Modifiable<M, T, B> entry,
+ final ResourceKey<M> key,
+ final @Nullable M oldNms,
+ final B builder,
+ RegistrationInfo registrationInfo,
+ final RegisterMethod<M, R> registerMethod,
+ final Conversions conversions
+ ) {
+ @Subst("namespace:key") final ResourceLocation beingAdded = key.location();
+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.apiKey(), Key.key(beingAdded.getNamespace(), beingAdded.getPath()));
+ final RegistryEntryAddEventImpl<T, B> event = entry.createEntryAddEvent(typedKey, builder, conversions);
+ LifecycleEventRunner.INSTANCE.callEvent(this.valueAddEventTypes.getEventType(entry.apiKey()), event);
+ if (oldNms != null) {
+ ((MappedRegistry<M>) registry).clearIntrusiveHolder(oldNms);
+ }
+ final M newNms = event.builder().build();
+ if (oldNms != null && !newNms.equals(oldNms)) {
+ registrationInfo = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
+ }
+ return registerMethod.register((WritableRegistry<M>) registry, key, newNms, registrationInfo);
+ }
+
+ private static <M> M registerWithInstance(final WritableRegistry<M> writableRegistry, final ResourceKey<M> key, final M value, final RegistrationInfo registrationInfo) {
+ writableRegistry.register(key, value, registrationInfo);
+ return value;
+ }
+
+ @FunctionalInterface
+ public interface RegisterMethod<M, R> {
+
+ R register(WritableRegistry<M> writableRegistry, ResourceKey<M> key, M value, RegistrationInfo registrationInfo);
+ }
+
+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void runFreezeListeners(final ResourceKey<? extends Registry<M>> resourceKey, final Conversions conversions) {
+ final @Nullable RegistryEntryInfo<M, T> entry = PaperRegistries.getEntry(resourceKey);
+ if (!RegistryEntry.Addable.isAddable(entry) || !this.freezeEventTypes.hasHandlers(entry.apiKey())) {
+ return;
+ }
+ final RegistryEntry.Addable<M, T, B> writableEntry = RegistryEntry.Addable.asAddable(entry);
+ final WritableCraftRegistry<M, T, B> writableRegistry = PaperRegistryAccess.instance().getWritableRegistry(entry.apiKey());
+ final RegistryFreezeEventImpl<T, B> event = writableEntry.createFreezeEvent(writableRegistry, conversions);
+ LifecycleEventRunner.INSTANCE.callEvent(this.freezeEventTypes.getEventType(entry.apiKey()), event);
+ }
+
+ public <T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> getRegistryValueAddEventType(final RegistryEventProvider<T, B> type) {
+ if (!RegistryEntry.Modifiable.isModifiable(PaperRegistries.getEntry(type.registryKey()))) {
+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryEntryAddEvent");
+ }
+ return this.valueAddEventTypes.getOrCreate(type.registryKey(), RegistryEntryAddEventTypeImpl::new);
+ }
+
+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> getRegistryFreezeEventType(final RegistryEventProvider<T, B> type) {
+ if (!RegistryEntry.Addable.isAddable(PaperRegistries.getEntry(type.registryKey()))) {
+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryFreezeEvent");
+ }
+ return this.freezeEventTypes.getOrCreate(type.registryKey(), RegistryLifecycleEventType::new);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..78317c7ab42a666f19634593a8f3b696700764c8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
@@ -0,0 +1,92 @@
+package io.papermc.paper.registry;
+
+import com.mojang.serialization.Lifecycle;
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.registry.data.util.Conversions;
+import io.papermc.paper.registry.entry.RegistryEntry;
+import io.papermc.paper.registry.event.WritableRegistry;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.RegistrationInfo;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.CraftRegistry;
+import org.bukkit.craftbukkit.util.ApiVersion;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistry<T, M> {
+
+ private static final RegistrationInfo FROM_PLUGIN = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
+
+ private final RegistryEntry.BuilderHolder<M, T, B> entry;
+ private final MappedRegistry<M> registry;
+ private final PaperRegistryBuilder.Factory<M, T, ? extends B> builderFactory;
+ private final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit;
+
+ public WritableCraftRegistry(
+ final RegistryEntry.BuilderHolder<M, T, B> entry,
+ final Class<?> classToPreload,
+ final MappedRegistry<M> registry,
+ final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater,
+ final PaperRegistryBuilder.Factory<M, T, ? extends B> builderFactory,
+ final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit
+ ) {
+ super(classToPreload, registry, null, serializationUpdater);
+ this.entry = entry;
+ this.registry = registry;
+ this.builderFactory = builderFactory;
+ this.minecraftToBukkit = minecraftToBukkit;
+ }
+
+ public void register(final TypedKey<T> key, final Consumer<? super B> value, final Conversions conversions) {
+ final ResourceKey<M> resourceKey = ResourceKey.create(this.registry.key(), PaperAdventure.asVanilla(key.key()));
+ this.registry.validateWrite(resourceKey);
+ final B builder = this.newBuilder(conversions, key);
+ value.accept(builder);
+ PaperRegistryListenerManager.INSTANCE.registerWithListeners(
+ this.registry,
+ RegistryEntry.Modifiable.asModifiable(this.entry),
+ resourceKey,
+ builder,
+ FROM_PLUGIN,
+ conversions
+ );
+ }
+
+ @Override
+ public final @Nullable T createBukkit(final NamespacedKey namespacedKey, final @Nullable M minecraft) {
+ if (minecraft == null) {
+ return null;
+ }
+ return this.minecraftToBukkit(namespacedKey, minecraft);
+ }
+
+ public WritableRegistry<T, B> createApiWritableRegistry(final Conversions conversions) {
+ return new ApiWritableRegistry(conversions);
+ }
+
+ public T minecraftToBukkit(final NamespacedKey namespacedKey, final M minecraft) {
+ return this.minecraftToBukkit.apply(namespacedKey, minecraft);
+ }
+
+ protected B newBuilder(final Conversions conversions, final TypedKey<T> key) {
+ return this.builderFactory.create(conversions, key);
+ }
+
+ public class ApiWritableRegistry implements WritableRegistry<T, B> {
+
+ private final Conversions conversions;
+
+ public ApiWritableRegistry(final Conversions conversions) {
+ this.conversions = conversions;
+ }
+
+ @Override
+ public void register(final TypedKey<T> key, final Consumer<? super B> value) {
+ WritableCraftRegistry.this.register(key, value, this.conversions);
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/data/util/Conversions.java b/src/main/java/io/papermc/paper/registry/data/util/Conversions.java
new file mode 100644
index 0000000000000000000000000000000000000000..eda5cc7d45ef59ccc1c9c7e027c1f044f1dcc86b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/util/Conversions.java
@@ -0,0 +1,36 @@
+package io.papermc.paper.registry.data.util;
+
+import com.mojang.serialization.JavaOps;
+import io.papermc.paper.adventure.WrapperAwareSerializer;
+import net.kyori.adventure.text.Component;
+import net.minecraft.resources.RegistryOps;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.Contract;
+
+@DefaultQualifier(NonNull.class)
+public class Conversions {
+
+ private final RegistryOps.RegistryInfoLookup lookup;
+ private final WrapperAwareSerializer serializer;
+
+ public Conversions(final RegistryOps.RegistryInfoLookup lookup) {
+ this.lookup = lookup;
+ this.serializer = new WrapperAwareSerializer(() -> RegistryOps.create(JavaOps.INSTANCE, lookup));
+ }
+
+ public RegistryOps.RegistryInfoLookup lookup() {
+ return this.lookup;
+ }
+
+ @Contract("null -> null; !null -> !null")
+ public net.minecraft.network.chat.@Nullable Component asVanilla(final @Nullable Component adventure) {
+ if (adventure == null) return null;
+ return this.serializer.serialize(adventure);
+ }
+
+ public Component asAdventure(final net.minecraft.network.chat.@Nullable Component vanilla) {
+ return vanilla == null ? Component.empty() : this.serializer.deserialize(vanilla);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..aeec9b3ae2911f041d000b3db72f37974020ba60
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java
@@ -0,0 +1,44 @@
+package io.papermc.paper.registry.entry;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryHolder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.WritableCraftRegistry;
+import io.papermc.paper.registry.data.util.Conversions;
+import java.util.function.BiFunction;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+
+public class AddableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistryEntry<M, T> implements RegistryEntry.Addable<M, T, B> {
+
+ private final PaperRegistryBuilder.Filler<M, T, B> builderFiller;
+
+ protected AddableRegistryEntry(
+ final ResourceKey<? extends Registry<M>> mcKey,
+ final RegistryKey<T> apiKey,
+ final Class<?> classToPreload,
+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
+ ) {
+ super(mcKey, apiKey, classToPreload, minecraftToBukkit);
+ this.builderFiller = builderFiller;
+ }
+
+ private WritableCraftRegistry<M, T, B> createRegistry(final Registry<M> registry) {
+ return new WritableCraftRegistry<>(this, this.classToPreload, (MappedRegistry<M>) registry, this.updater, this.builderFiller.asFactory(), this.minecraftToBukkit);
+ }
+
+ @Override
+ public RegistryHolder<T> createRegistryHolder(final Registry<M> nmsRegistry) {
+ return new RegistryHolder.Memoized<>(() -> this.createRegistry(nmsRegistry));
+ }
+
+ @Override
+ public B fillBuilder(final Conversions conversions, final TypedKey<T> key, final M nms) {
+ return this.builderFiller.fill(conversions, key, nms);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..515a995e3862f8e7cb93d149315ea32e04a08716
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java
@@ -0,0 +1,32 @@
+package io.papermc.paper.registry.entry;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.data.util.Conversions;
+import java.util.function.BiFunction;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+
+public class ModifiableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistryEntry<M, T> implements RegistryEntry.Modifiable<M, T, B> {
+
+ protected final PaperRegistryBuilder.Filler<M, T, B> builderFiller;
+
+ protected ModifiableRegistryEntry(
+ final ResourceKey<? extends Registry<M>> mcKey,
+ final RegistryKey<T> apiKey,
+ final Class<?> toPreload,
+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
+ ) {
+ super(mcKey, apiKey, toPreload, minecraftToBukkit);
+ this.builderFiller = builderFiller;
+ }
+
+ @Override
+ public B fillBuilder(final Conversions conversions, final TypedKey<T> key, final M nms) {
+ return this.builderFiller.fill(conversions, key, nms);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
index 15991bf13894d850f360a520d1815711d25973ec..f2e919705301cb23ed1938ca3c1976378249172c 100644
--- a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
+++ b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
@@ -1,7 +1,13 @@
package io.papermc.paper.registry.entry;
+import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.RegistryHolder;
import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.WritableCraftRegistry;
+import io.papermc.paper.registry.data.util.Conversions;
+import io.papermc.paper.registry.event.RegistryEntryAddEventImpl;
+import io.papermc.paper.registry.event.RegistryFreezeEventImpl;
import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
import java.util.function.BiFunction;
import java.util.function.Supplier;
@@ -11,6 +17,7 @@ import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
@DefaultQualifier(NonNull.class)
@@ -32,6 +39,65 @@ public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M,
return new DelayedRegistryEntry<>(this);
}
+ interface BuilderHolder<M, T, B extends PaperRegistryBuilder<M, T>> extends RegistryEntryInfo<M, T> {
+
+ B fillBuilder(Conversions conversions, TypedKey<T> key, M nms);
+ }
+
+ /**
+ * Can mutate values being added to the registry
+ */
+ interface Modifiable<M, T, B extends PaperRegistryBuilder<M, T>> extends BuilderHolder<M, T, B> {
+
+ static boolean isModifiable(final @Nullable RegistryEntryInfo<?, ?> entry) {
+ return entry instanceof RegistryEntry.Modifiable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Modifiable<?, ?, ?>);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Modifiable<M, T, B> asModifiable(final RegistryEntryInfo<M, T> entry) { // TODO remove Keyed
+ return (Modifiable<M, T, B>) possiblyUnwrap(entry);
+ }
+
+ default RegistryEntryAddEventImpl<T, B> createEntryAddEvent(final TypedKey<T> key, final B initialBuilder, final Conversions conversions) {
+ return new RegistryEntryAddEventImpl<>(key, initialBuilder, this.apiKey(), conversions);
+ }
+ }
+
+ /**
+ * Can only add new values to the registry, not modify any values.
+ */
+ interface Addable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends BuilderHolder<M, T, B> { // TODO remove Keyed
+
+ default RegistryFreezeEventImpl<T, B> createFreezeEvent(final WritableCraftRegistry<M, T, B> writableRegistry, final Conversions conversions) {
+ return new RegistryFreezeEventImpl<>(this.apiKey(), writableRegistry.createApiWritableRegistry(conversions), conversions);
+ }
+
+ static boolean isAddable(final @Nullable RegistryEntryInfo<?, ?> entry) {
+ return entry instanceof RegistryEntry.Addable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Addable<?, ?, ?>);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Addable<M, T, B> asAddable(final RegistryEntryInfo<M, T> entry) {
+ return (Addable<M, T, B>) possiblyUnwrap(entry);
+ }
+ }
+
+ /**
+ * Can mutate values and add new values.
+ */
+ interface Writable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends Modifiable<M, T, B>, Addable<M, T, B> { // TODO remove Keyed
+
+ static boolean isWritable(final @Nullable RegistryEntryInfo<?, ?> entry) {
+ return entry instanceof RegistryEntry.Writable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Writable<?, ?, ?>);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Writable<M, T, B> asWritable(final RegistryEntryInfo<M, T> entry) { // TODO remove Keyed
+ return (Writable<M, T, B>) possiblyUnwrap(entry);
+ }
+ }
+
+ private static <M, B extends Keyed> RegistryEntryInfo<M, B> possiblyUnwrap(final RegistryEntryInfo<M, B> entry) {
+ return entry instanceof final DelayedRegistryEntry<M, B> delayed ? delayed.delegate() : entry;
+ }
+
static <M, B extends Keyed> RegistryEntry<M, B> entry(
final ResourceKey<? extends Registry<M>> mcKey,
final RegistryKey<B> apiKey,
@@ -48,4 +114,24 @@ public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M,
) {
return new ApiRegistryEntry<>(mcKey, apiKey, apiRegistrySupplier);
}
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> modifiable(
+ final ResourceKey<? extends Registry<M>> mcKey,
+ final RegistryKey<T> apiKey,
+ final Class<?> toPreload,
+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
+ final PaperRegistryBuilder.Filler<M, T, B> filler
+ ) {
+ return new ModifiableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler);
+ }
+
+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> writable(
+ final ResourceKey<? extends Registry<M>> mcKey,
+ final RegistryKey<T> apiKey,
+ final Class<?> toPreload,
+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
+ final PaperRegistryBuilder.Filler<M, T, B> filler
+ ) {
+ return new WritableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler);
+ }
}
diff --git a/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..562accce731630327d116afd1c9d559df7e386bd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
@@ -0,0 +1,22 @@
+package io.papermc.paper.registry.entry;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import java.util.function.BiFunction;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+
+public class WritableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends AddableRegistryEntry<M, T, B> implements RegistryEntry.Writable<M, T, B> { // TODO remove Keyed
+
+ protected WritableRegistryEntry(
+ final ResourceKey<? extends Registry<M>> mcKey,
+ final RegistryKey<T> apiKey,
+ final Class<?> classToPreload,
+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
+ ) {
+ super(mcKey, apiKey, classToPreload, minecraftToBukkit, builderFiller);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc9c8fd313f530777af80ad79e03903f3f8f9829
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java
@@ -0,0 +1,30 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
+import io.papermc.paper.registry.PaperRegistries;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.data.util.Conversions;
+import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
+import io.papermc.paper.registry.tag.Tag;
+import io.papermc.paper.registry.tag.TagKey;
+import net.minecraft.core.HolderSet;
+import net.minecraft.resources.RegistryOps;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public record RegistryEntryAddEventImpl<T, B extends RegistryBuilder<T>>(
+ TypedKey<T> key,
+ B builder,
+ RegistryKey<T> registryKey,
+ Conversions conversions
+) implements RegistryEntryAddEvent<T, B>, PaperLifecycleEvent {
+
+ @Override
+ public @NonNull <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) {
+ final RegistryOps.RegistryInfo<Object> registryInfo = this.conversions.lookup().lookup(PaperRegistries.registryToNms(tagKey.registryKey())).orElseThrow();
+ final HolderSet.Named<?> tagSet = registryInfo.getter().getOrThrow(PaperRegistries.toNms(tagKey));
+ return new NamedRegistryKeySetImpl<>(tagKey, tagSet);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfcd0884d778ca62817fb1d22f0f2ed1f2abe855
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
@@ -0,0 +1,45 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
+import io.papermc.paper.registry.RegistryKey;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+public final class RegistryEventMap {
+
+ private final Map<RegistryKey<?>, LifecycleEventType<BootstrapContext, ? extends LifecycleEvent, ?>> eventTypes = new HashMap<>();
+ private final String name;
+
+ public RegistryEventMap(final String name) {
+ this.name = name;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T, E extends LifecycleEvent, ET extends LifecycleEventType<BootstrapContext, E, ?>> ET getOrCreate(final RegistryKey<T> registryKey, final BiFunction<? super RegistryKey<T>, ? super String, ET> eventTypeCreator) {
+ final ET eventType;
+ if (this.eventTypes.containsKey(registryKey)) {
+ eventType = (ET) this.eventTypes.get(registryKey);
+ } else {
+ eventType = eventTypeCreator.apply(registryKey, this.name);
+ this.eventTypes.put(registryKey, eventType);
+ }
+ return eventType;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T, E extends LifecycleEvent> LifecycleEventType<BootstrapContext, E, ?> getEventType(final RegistryKey<T> registryKey) {
+ return (LifecycleEventType<BootstrapContext, E, ?>) Objects.requireNonNull(this.eventTypes.get(registryKey), "No hook for " + registryKey);
+ }
+
+ public boolean hasHandlers(final RegistryKey<?> registryKey) {
+ final AbstractLifecycleEventType<?, ?, ?> type = ((AbstractLifecycleEventType<?, ?, ?>) this.eventTypes.get(registryKey));
+ return type != null && type.hasHandlers();
+ }
+
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..34c842ffa355e3c8001dd7b8551bcb49229a6391
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.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.PaperRegistryListenerManager;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
+
+public class RegistryEventTypeProviderImpl implements RegistryEventTypeProvider {
+
+ public static RegistryEventTypeProviderImpl instance() {
+ return (RegistryEventTypeProviderImpl) RegistryEventTypeProvider.provider();
+ }
+
+ @Override
+ public <T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> registryEntryAdd(final RegistryEventProvider<T, B> type) {
+ return PaperRegistryListenerManager.INSTANCE.getRegistryValueAddEventType(type);
+ }
+
+ @Override
+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> registryFreeze(final RegistryEventProvider<T, B> type) {
+ return PaperRegistryListenerManager.INSTANCE.getRegistryFreezeEventType(type);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..63957d2509e68ccc6eb2fd9ecaa35bfad7b71b81
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java
@@ -0,0 +1,28 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
+import io.papermc.paper.registry.PaperRegistries;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.data.util.Conversions;
+import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
+import io.papermc.paper.registry.tag.Tag;
+import io.papermc.paper.registry.tag.TagKey;
+import net.minecraft.core.HolderSet;
+import net.minecraft.resources.RegistryOps;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public record RegistryFreezeEventImpl<T, B extends RegistryBuilder<T>>(
+ RegistryKey<T> registryKey,
+ WritableRegistry<T, B> registry,
+ Conversions conversions
+) implements RegistryFreezeEvent<T, B>, PaperLifecycleEvent {
+
+ @Override
+ public @NonNull <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) {
+ final RegistryOps.RegistryInfo<Object> registryInfo = this.conversions.lookup().lookup(PaperRegistries.registryToNms(tagKey.registryKey())).orElseThrow();
+ final HolderSet.Named<?> tagSet = registryInfo.getter().getOrThrow(PaperRegistries.toNms(tagKey));
+ return new NamedRegistryKeySetImpl<>(tagKey, tagSet);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/package-info.java b/src/main/java/io/papermc/paper/registry/event/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..14d2d9766b8dee763f220c397aba3ad432d02aaa
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.registry.event;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbf853bf1cbb3c7bbef531afe185818b9454299b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java
@@ -0,0 +1,37 @@
+package io.papermc.paper.registry.event.type;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class RegistryEntryAddEventTypeImpl<T, B extends RegistryBuilder<T>> extends PrioritizableLifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, RegistryEntryAddConfiguration<T>> implements RegistryEntryAddEventType<T, B> {
+
+ public RegistryEntryAddEventTypeImpl(final RegistryKey<T> registryKey, final String eventName) {
+ super(registryKey + " / " + eventName, BootstrapContext.class);
+ }
+
+ @Override
+ public boolean blocksReloading(final BootstrapContext eventOwner) {
+ return false; // only runs once
+ }
+
+ @Override
+ public RegistryEntryAddConfiguration<T> newHandler(final LifecycleEventHandler<? super RegistryEntryAddEvent<T, B>> handler) {
+ return new RegistryEntryAddHandlerConfiguration<>(handler, this);
+ }
+
+ @Override
+ public void forEachHandler(final RegistryEntryAddEvent<T, B> event, final Consumer<RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>>> consumer, final Predicate<RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>>> predicate) {
+ super.forEachHandler(event, consumer, predicate.and(handler -> this.matchesTarget(event, handler)));
+ }
+
+ private boolean matchesTarget(final RegistryEntryAddEvent<T, B> event, final RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>> handler) {
+ final RegistryEntryAddHandlerConfiguration<T, B> config = (RegistryEntryAddHandlerConfiguration<T, B>) handler.config();
+ return config.filter() == null || config.filter().test(event.key());
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..548f5bf979e88708e98d04dfe22ccaa300c91ddd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.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.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfigurationImpl;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
+import java.util.function.Predicate;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.Contract;
+
+public class RegistryEntryAddHandlerConfiguration<T, B extends RegistryBuilder<T>> extends PrioritizedLifecycleEventHandlerConfigurationImpl<BootstrapContext, RegistryEntryAddEvent<T, B>> implements RegistryEntryAddConfiguration<T> {
+
+ private @Nullable Predicate<TypedKey<T>> filter;
+
+ public RegistryEntryAddHandlerConfiguration(final LifecycleEventHandler<? super RegistryEntryAddEvent<T, B>> handler, final AbstractLifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, ?> eventType) {
+ super(handler, eventType);
+ }
+
+ @Contract(pure = true)
+ public @Nullable Predicate<TypedKey<T>> filter() {
+ return this.filter;
+ }
+
+ @Override
+ public RegistryEntryAddConfiguration<T> filter(final Predicate<TypedKey<T>> filter) {
+ this.filter = filter;
+ return this;
+ }
+
+ @Override
+ public RegistryEntryAddConfiguration<T> priority(final int priority) {
+ return (RegistryEntryAddConfiguration<T>) super.priority(priority);
+ }
+
+ @Override
+ public RegistryEntryAddConfiguration<T> monitor() {
+ return (RegistryEntryAddConfiguration<T>) super.monitor();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ee77022198bf5f9f88c6a1917a1da30b1863883
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.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.PrioritizableLifecycleEventType;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.event.RegistryEvent;
+
+public final class RegistryLifecycleEventType<T, E extends RegistryEvent<T>> extends PrioritizableLifecycleEventType.Simple<BootstrapContext, E> {
+
+ public RegistryLifecycleEventType(final RegistryKey<T> registryKey, final String eventName) {
+ super(registryKey + " / " + eventName, BootstrapContext.class);
+ }
+
+ @Override
+ public boolean blocksReloading(final BootstrapContext eventOwner) {
+ return false; // only runs once
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
index 9400fed345344a0a8e4fb301cca6a1867adf625b..0cdc92acd3ebb6efb10e1b66419cc05618301581 100644
--- a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
+++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
@@ -1,5 +1,7 @@
package io.papermc.paper.registry.legacy;
+import io.papermc.paper.registry.tag.Tag;
+import io.papermc.paper.registry.tag.TagKey;
import java.util.Iterator;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -7,7 +9,6 @@ import net.kyori.adventure.key.Key;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
-import org.bukkit.craftbukkit.CraftRegistry;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
@@ -58,4 +59,14 @@ public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> imple
public NamespacedKey getKey(final T value) {
return this.delegate().getKey(value);
}
+
+ @Override
+ public boolean hasTag(final TagKey<T> key) {
+ return this.delegate().hasTag(key);
+ }
+
+ @Override
+ public @NotNull Tag<T> getTag(final TagKey<T> key) {
+ return this.delegate().getTag(key);
+ }
}
diff --git a/src/main/java/io/papermc/paper/registry/package-info.java b/src/main/java/io/papermc/paper/registry/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b80179ff90e085568d7ceafd9b17511789dc99b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.registry;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java b/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8c2c18a1ed5cd587266bd415170610781531a12
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java
@@ -0,0 +1,76 @@
+package io.papermc.paper.registry.set;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import io.papermc.paper.registry.PaperRegistries;
+import io.papermc.paper.registry.RegistryAccess;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.tag.Tag;
+import io.papermc.paper.registry.tag.TagKey;
+import java.util.Collection;
+import java.util.Set;
+import net.kyori.adventure.key.Key;
+import net.minecraft.core.Holder;
+import net.minecraft.core.HolderSet;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Unmodifiable;
+
+@DefaultQualifier(NonNull.class)
+public record NamedRegistryKeySetImpl<T extends Keyed, M>( // TODO remove Keyed
+ TagKey<T> tagKey,
+ HolderSet.Named<M> namedSet
+) implements Tag<T>, org.bukkit.Tag<T> {
+
+ @Override
+ public @Unmodifiable Collection<TypedKey<T>> values() {
+ final ImmutableList.Builder<TypedKey<T>> builder = ImmutableList.builder();
+ for (final Holder<M> holder : this.namedSet) {
+ builder.add(TypedKey.create(this.tagKey.registryKey(), CraftNamespacedKey.fromMinecraft(((Holder.Reference<?>) holder).key().location())));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public RegistryKey<T> registryKey() {
+ return this.tagKey.registryKey();
+ }
+
+ @Override
+ public boolean contains(final TypedKey<T> valueKey) {
+ return Iterables.any(this.namedSet, h -> {
+ return PaperRegistries.fromNms(((Holder.Reference<?>) h).key()).equals(valueKey);
+ });
+ }
+
+ @Override
+ public @Unmodifiable Collection<T> resolve(final Registry<T> registry) {
+ final ImmutableList.Builder<T> builder = ImmutableList.builder();
+ for (final Holder<M> holder : this.namedSet) {
+ builder.add(registry.getOrThrow(CraftNamespacedKey.fromMinecraft(((Holder.Reference<?>) holder).key().location())));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public boolean isTagged(final T item) {
+ return this.getValues().contains(item);
+ }
+
+ @Override
+ public Set<T> getValues() {
+ return Set.copyOf(this.resolve(RegistryAccess.registryAccess().getRegistry(this.registryKey())));
+ }
+
+ @Override
+ public @NotNull NamespacedKey getKey() {
+ final Key key = this.tagKey().key();
+ return new NamespacedKey(key.namespace(), key.value());
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java b/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java
new file mode 100644
index 0000000000000000000000000000000000000000..f09ce9c8547ef05153847245746473dd9a8acbe6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java
@@ -0,0 +1,48 @@
+package io.papermc.paper.registry.set;
+
+import io.papermc.paper.registry.PaperRegistries;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.core.Holder;
+import net.minecraft.core.HolderSet;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class PaperRegistrySets {
+
+ public static <A extends Keyed, M> HolderSet<M> convertToNms(final ResourceKey<? extends Registry<M>> resourceKey, final RegistryOps.RegistryInfoLookup lookup, final RegistryKeySet<A> registryKeySet) { // TODO remove Keyed
+ if (registryKeySet instanceof NamedRegistryKeySetImpl<A, ?>) {
+ return ((NamedRegistryKeySetImpl<A, M>) registryKeySet).namedSet();
+ } else {
+ final RegistryOps.RegistryInfo<M> registryInfo = lookup.lookup(resourceKey).orElseThrow();
+ return HolderSet.direct(key -> {
+ return registryInfo.getter().getOrThrow(PaperRegistries.toNms(key));
+ }, registryKeySet.values());
+ }
+ }
+
+ public static <A extends Keyed, M> RegistryKeySet<A> convertToApi(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
+ if (holders instanceof final HolderSet.Named<M> named) {
+ return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
+ } else {
+ final List<TypedKey<A>> keys = new ArrayList<>();
+ for (final Holder<M> holder : holders) {
+ if (!(holder instanceof final Holder.Reference<M> reference)) {
+ throw new UnsupportedOperationException("Cannot convert a holder set containing direct holders");
+ }
+ keys.add(PaperRegistries.fromNms(reference.key()));
+ }
+ return RegistrySet.keySet(registryKey, keys);
+ }
+ }
+
+ private PaperRegistrySets() {
+ }
+}
diff --git a/src/main/java/net/minecraft/core/MappedRegistry.java b/src/main/java/net/minecraft/core/MappedRegistry.java
index 71e04e5c1bc0722abf8ca2e0738bd60b6d7ae21c..063630c1ffcce099139c59d598fc5a210e21f640 100644
--- a/src/main/java/net/minecraft/core/MappedRegistry.java
+++ b/src/main/java/net/minecraft/core/MappedRegistry.java
@@ -509,4 +509,12 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
Stream<HolderSet.Named<T>> getTags();
}
+ // Paper start
+ // used to clear intrusive holders from GameEvent, Item, Block, EntityType, and Fluid from unused instances of those types
+ public void clearIntrusiveHolder(final T instance) {
+ if (this.unregisteredIntrusiveHolders != null) {
+ this.unregisteredIntrusiveHolders.remove(instance);
+ }
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
index 4638ba98dbbdb0f880337347be85a6e0fbed2191..bc448f8511c629d1f13d4baf717a11e6a6ad24f9 100644
--- a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
+++ b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
@@ -296,6 +296,17 @@ public class BuiltInRegistries {
public static final Registry<SlotDisplay.Type<?>> SLOT_DISPLAY = registerSimple(Registries.SLOT_DISPLAY, SlotDisplays::bootstrap);
public static final Registry<RecipeBookCategory> RECIPE_BOOK_CATEGORY = registerSimple(Registries.RECIPE_BOOK_CATEGORY, RecipeBookCategories::bootstrap);
public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
+ // Paper start - add built-in registry conversions
+ public static final io.papermc.paper.registry.data.util.Conversions BUILT_IN_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(new net.minecraft.resources.RegistryOps.RegistryInfoLookup() {
+ @Override
+ public <T> java.util.Optional<net.minecraft.resources.RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryRef) {
+ final Registry<T> registry = net.minecraft.server.RegistryLayer.STATIC_ACCESS.lookupOrThrow(registryRef);
+ return java.util.Optional.of(
+ new net.minecraft.resources.RegistryOps.RegistryInfo<>(registry, registry, Lifecycle.experimental())
+ );
+ }
+ });
+ // Paper end - add built-in registry conversions
private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> key, BuiltInRegistries.RegistryBootstrap<T> initializer) {
return internalRegister(key, new MappedRegistry<>(key, Lifecycle.stable(), false), initializer);
@@ -336,6 +347,7 @@ public class BuiltInRegistries {
}
public static void bootStrap(Runnable runnable) {
// Paper end
+ REGISTRY.freeze(); // Paper - freeze main registry early
createContents();
runnable.run(); // Paper
freeze();
@@ -355,6 +367,7 @@ public class BuiltInRegistries {
for (Registry<?> registry : REGISTRY) {
bindBootstrappedTagsToEmpty(registry);
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BUILT_IN_CONVERSIONS); // Paper
registry.freeze();
}
}
diff --git a/src/main/java/net/minecraft/resources/RegistryDataLoader.java b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
index 61774165a25209ee6d26cf8d80149b220c3874e6..fdc88e52235a152dbe3cca273990b4b68f8daaf8 100644
--- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java
+++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
@@ -130,7 +130,7 @@ public class RegistryDataLoader {
public static RegistryAccess.Frozen load(
ResourceManager resourceManager, List<HolderLookup.RegistryLookup<?>> registries, List<RegistryDataLoader.RegistryData<?>> entries
) {
- return load((loader, infoGetter) -> loader.loadFromResources(resourceManager, infoGetter), registries, entries);
+ return load((loader, infoGetter, conversions) -> loader.loadFromResources(resourceManager, infoGetter, conversions), registries, entries); // Paper - pass conversions
}
public static RegistryAccess.Frozen load(
@@ -139,7 +139,7 @@ public class RegistryDataLoader {
List<HolderLookup.RegistryLookup<?>> registries,
List<RegistryDataLoader.RegistryData<?>> entries
) {
- return load((loader, infoGetter) -> loader.loadFromNetwork(data, factory, infoGetter), registries, entries);
+ return load((loader, infoGetter, conversions) -> loader.loadFromNetwork(data, factory, infoGetter, conversions), registries, entries); // Paper - pass conversions
}
private static RegistryAccess.Frozen load(
@@ -148,9 +148,11 @@ public class RegistryDataLoader {
Map<ResourceKey<?>, Exception> map = new HashMap<>();
List<RegistryDataLoader.Loader<?>> list = entries.stream().map(entry -> entry.create(Lifecycle.stable(), map)).collect(Collectors.toUnmodifiableList());
RegistryOps.RegistryInfoLookup registryInfoLookup = createContext(registries, list);
- list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader<?>)loader, registryInfoLookup));
+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryInfoLookup); // Paper - create conversions
+ list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader<?>)loader, registryInfoLookup, conversions));
list.forEach(loader -> {
Registry<?> registry = loader.registry();
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(loader.registry.key(), conversions); // Paper - run pre-freeze listeners
try {
registry.freeze();
@@ -238,13 +240,13 @@ public class RegistryDataLoader {
}
private static <E> void loadElementFromResource(
- WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo
+ WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo, io.papermc.paper.registry.data.util.Conversions conversions
) throws IOException {
try (Reader reader = resource.openAsReader()) {
JsonElement jsonElement = JsonParser.parseReader(reader);
DataResult<E> dataResult = decoder.parse(ops, jsonElement);
E object = dataResult.getOrThrow();
- registry.register(key, object, entryInfo);
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(registry, key, object, entryInfo, conversions); // Paper - register with listeners
}
}
@@ -253,7 +255,8 @@ public class RegistryDataLoader {
RegistryOps.RegistryInfoLookup infoGetter,
WritableRegistry<E> registry,
Decoder<E> elementDecoder,
- Map<ResourceKey<?>, Exception> errors
+ Map<ResourceKey<?>, Exception> errors,
+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
) {
String string = Registries.elementsDirPath(registry.key());
FileToIdConverter fileToIdConverter = FileToIdConverter.json(string);
@@ -266,7 +269,7 @@ public class RegistryDataLoader {
RegistrationInfo registrationInfo = REGISTRATION_INFO_CACHE.apply(resource.knownPackInfo());
try {
- loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo);
+ loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo, conversions); // Paper - pass conversions
} catch (Exception var15) {
errors.put(
resourceKey,
@@ -284,7 +287,8 @@ public class RegistryDataLoader {
RegistryOps.RegistryInfoLookup infoGetter,
WritableRegistry<E> registry,
Decoder<E> decoder,
- Map<ResourceKey<?>, Exception> loadingErrors
+ Map<ResourceKey<?>, Exception> loadingErrors,
+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
) {
RegistryDataLoader.NetworkedRegistryData networkedRegistryData = data.get(registry.key());
if (networkedRegistryData != null) {
@@ -311,7 +315,7 @@ public class RegistryDataLoader {
try {
Resource resource = factory.getResourceOrThrow(resourceLocation);
- loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO);
+ loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO, conversions); // Paper - pass conversions
} catch (Exception var18) {
loadingErrors.put(resourceKey, new IllegalStateException("Failed to parse local data", var18));
}
@@ -323,22 +327,23 @@ public class RegistryDataLoader {
}
static record Loader<T>(RegistryDataLoader.RegistryData<T> data, WritableRegistry<T> registry, Map<ResourceKey<?>, Exception> loadingErrors) {
- public void loadFromResources(ResourceManager resourceManager, RegistryOps.RegistryInfoLookup infoGetter) {
- RegistryDataLoader.loadContentsFromManager(resourceManager, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors);
+ public void loadFromResources(ResourceManager resourceManager, RegistryOps.RegistryInfoLookup infoGetter, io.papermc.paper.registry.data.util.Conversions conversions) { // Paper - pass conversions
+ RegistryDataLoader.loadContentsFromManager(resourceManager, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors, conversions); // Paper - pass conversions
}
public void loadFromNetwork(
Map<ResourceKey<? extends Registry<?>>, RegistryDataLoader.NetworkedRegistryData> data,
ResourceProvider factory,
- RegistryOps.RegistryInfoLookup infoGetter
+ RegistryOps.RegistryInfoLookup infoGetter,
+ io.papermc.paper.registry.data.util.Conversions conversions // Paper
) {
- RegistryDataLoader.loadContentsFromNetwork(data, factory, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors);
+ RegistryDataLoader.loadContentsFromNetwork(data, factory, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors, conversions); // Paper - pass conversions
}
}
@FunctionalInterface
interface LoadingFunction {
- void apply(RegistryDataLoader.Loader<?> loader, RegistryOps.RegistryInfoLookup infoGetter);
+ void apply(RegistryDataLoader.Loader<?> loader, RegistryOps.RegistryInfoLookup infoGetter, io.papermc.paper.registry.data.util.Conversions conversions); // Paper - pass conversions
}
public static record NetworkedRegistryData(List<RegistrySynchronization.PackedRegistryEntry> elements, TagNetworkSerialization.NetworkPayload tags) {
diff --git a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
index 6fddef967b6314ca0158f5bd4b8898670ea5e9ec..b5ca1a0acb16d0cd8dccc854f309d425a48b070d 100644
--- a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
+++ b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
@@ -50,8 +50,9 @@ public class ReloadableServerRegistries {
);
HolderLookup.Provider provider = HolderLookup.Provider.create(list.stream());
RegistryOps<JsonElement> registryOps = provider.createSerializationContext(JsonOps.INSTANCE);
+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryOps.lookupProvider); // Paper
List<CompletableFuture<WritableRegistry<?>>> list2 = LootDataType.values()
- .map(type -> scheduleRegistryLoad((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor))
+ .map(type -> scheduleRegistryLoad((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor, conversions)) // Paper
.toList();
CompletableFuture<List<WritableRegistry<?>>> completableFuture = Util.sequence(list2);
return completableFuture.thenApplyAsync(
@@ -60,7 +61,7 @@ public class ReloadableServerRegistries {
}
private static <T> CompletableFuture<WritableRegistry<?>> scheduleRegistryLoad(
- LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor
+ LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor, io.papermc.paper.registry.data.util.Conversions conversions // Paper
) {
return CompletableFuture.supplyAsync(() -> {
WritableRegistry<T> writableRegistry = new MappedRegistry<>(type.registryKey(), Lifecycle.experimental());
@@ -68,7 +69,7 @@ public class ReloadableServerRegistries {
Map<ResourceLocation, T> map = new HashMap<>();
String string = Registries.elementsDirPath(type.registryKey());
SimpleJsonResourceReloadListener.scanDirectory(resourceManager, string, ops, type.codec(), map);
- map.forEach((id, value) -> writableRegistry.register(ResourceKey.create(type.registryKey(), id), (T)value, DEFAULT_REGISTRATION_INFO));
+ map.forEach((id, value) -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(writableRegistry, ResourceKey.create(type.registryKey(), id), value, DEFAULT_REGISTRATION_INFO, conversions)); // Paper - register with listeners
TagLoader.loadTagsForRegistry(resourceManager, writableRegistry);
return writableRegistry;
}, prepareExecutor);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index 45c78c113e881b277e1216293ad918ee40b44325..8314059455d91f01b986c5c0a239f41817834bd6 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -164,11 +164,11 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
private final Map<NamespacedKey, B> cache = new HashMap<>();
private final Map<B, NamespacedKey> byValue = new java.util.IdentityHashMap<>(); // Paper - improve Registry
private final net.minecraft.core.Registry<M> minecraftRegistry;
- private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
+ private final BiFunction<? super NamespacedKey, M, B> minecraftToBukkit; // Paper
private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater; // Paper - rename to make it *clear* what it is *only* for
private boolean init;
- public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
+ public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<? super NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
this.bukkitClass = bukkitClass;
this.minecraftRegistry = minecraftRegistry;
this.minecraftToBukkit = minecraftToBukkit;
@@ -251,4 +251,17 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return this.byValue.get(value);
}
// Paper end - improve Registry
+
+ // Paper start - RegistrySet API
+ @Override
+ public boolean hasTag(final io.papermc.paper.registry.tag.TagKey<B> key) {
+ return this.minecraftRegistry.get(net.minecraft.tags.TagKey.create(this.minecraftRegistry.key(), io.papermc.paper.adventure.PaperAdventure.asVanilla(key.key()))).isPresent();
+ }
+
+ @Override
+ public io.papermc.paper.registry.tag.Tag<B> getTag(final io.papermc.paper.registry.tag.TagKey<B> key) {
+ final net.minecraft.core.HolderSet.Named<M> namedHolderSet = this.minecraftRegistry.get(io.papermc.paper.registry.PaperRegistries.toNms(key)).orElseThrow();
+ return new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(key, namedHolderSet);
+ }
+ // Paper end - RegistrySet API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index f0556a207f6d9d1766ef0d9753355f7fa5052dc1..f55733b7a56ac21cb297971b9e854ef54001ca26 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -666,6 +666,21 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
// Paper end - lifecycle event API
+ // Paper start - hack to get tags for non server-backed registries
+ @Override
+ public <A extends Keyed, M> io.papermc.paper.registry.tag.Tag<A> getTag(final io.papermc.paper.registry.tag.TagKey<A> tagKey) { // TODO remove Keyed
+ if (tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.ENTITY_TYPE && tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.FLUID) {
+ throw new UnsupportedOperationException(tagKey.registryKey() + " doesn't have tags");
+ }
+ final net.minecraft.resources.ResourceKey<? extends net.minecraft.core.Registry<M>> nmsKey = io.papermc.paper.registry.PaperRegistries.registryToNms(tagKey.registryKey());
+ final net.minecraft.core.Registry<M> nmsRegistry = org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().lookupOrThrow(nmsKey);
+ return nmsRegistry
+ .get(io.papermc.paper.registry.PaperRegistries.toNms(tagKey))
+ .map(named -> new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(tagKey, named))
+ .orElse(null);
+ }
+ // Paper end - hack to get tags for non server-backed registries
+
/**
* This helper class represents the different NBT Tags.
* <p>
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider b/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider
new file mode 100644
index 0000000000000000000000000000000000000000..8bee1a5ed877a04e4d027593df1f42cefdd824e7
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider
@@ -0,0 +1 @@
+io.papermc.paper.registry.event.RegistryEventTypeProviderImpl
diff --git a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ceef7b5864deb1d7c6aa5630febe86ba9427bdc4
--- /dev/null
+++ b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
@@ -0,0 +1,36 @@
+package io.papermc.paper.registry;
+
+import io.papermc.paper.registry.data.util.Conversions;
+import java.util.List;
+import java.util.Map;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.RegistryOps;
+import net.minecraft.resources.ResourceKey;
+import org.bukkit.support.RegistryHelper;
+import org.bukkit.support.environment.AllFeatures;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@AllFeatures
+class RegistryBuilderTest {
+
+ static List<Arguments> registries() {
+ return List.of(
+ );
+ }
+
+ @Disabled
+ @ParameterizedTest
+ @MethodSource("registries")
+ <M, T> void testEquality(final ResourceKey<? extends Registry<M>> resourceKey, final PaperRegistryBuilder.Filler<M, T, ?> filler) {
+ final Registry<M> registry = RegistryHelper.getRegistry().lookupOrThrow(resourceKey);
+ for (final Map.Entry<ResourceKey<M>, M> entry : registry.entrySet()) {
+ final M built = filler.fill(new Conversions(new RegistryOps.HolderLookupAdapter(RegistryHelper.getRegistry())), PaperRegistries.fromNms(entry.getKey()), entry.getValue()).build();
+ assertEquals(entry.getValue(), built);
+ }
+ }
+}
diff --git a/src/test/java/org/bukkit/registry/RegistryClassTest.java b/src/test/java/org/bukkit/registry/RegistryClassTest.java
index ea3d37f387bdb0dd5ae3fba9231ace31d0cebd64..c118c911972fe5a9f0c3e306009306f04ae2e821 100644
--- a/src/test/java/org/bukkit/registry/RegistryClassTest.java
+++ b/src/test/java/org/bukkit/registry/RegistryClassTest.java
@@ -111,7 +111,7 @@ public class RegistryClassTest {
outsideRequest.clear();
MockUtil.resetMock(spyRegistry);
doAnswer(invocation -> {
- Keyed item = realRegistry.get(invocation.getArgument(0));
+ Keyed item = realRegistry.get((NamespacedKey) invocation.getArgument(0)); // Paper - registry modification api - specifically call namespaced key overload
if (item == null) {
nullValue.add(invocation.getArgument(0));
@@ -120,10 +120,10 @@ public class RegistryClassTest {
nullAble.add(invocation.getArgument(0));
return item;
- }).when(spyRegistry).get(any());
+ }).when(spyRegistry).get((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
doAnswer(invocation -> {
- Keyed item = realRegistry.get(invocation.getArgument(0));
+ Keyed item = realRegistry.get((NamespacedKey) invocation.getArgument(0)); // Paper - registry modification api - specifically call namespaced key overload
if (item == null) {
nullValue.add(invocation.getArgument(0));
@@ -138,7 +138,7 @@ public class RegistryClassTest {
notNullAble.add(invocation.getArgument(0));
return item;
- }).when(spyRegistry).getOrThrow(any());
+ }).when(spyRegistry).getOrThrow((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
// Load class
try {
@@ -171,13 +171,13 @@ public class RegistryClassTest {
outsideRequest
.computeIfAbsent(type, ty -> new ArrayList<>()).add(invocation.getArgument(0));
return mock(type);
- }).when(spyRegistry).get(any());
+ }).when(spyRegistry).get((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
doAnswer(invocation -> {
outsideRequest
.computeIfAbsent(type, ty -> new ArrayList<>()).add(invocation.getArgument(0));
return mock(type);
- }).when(spyRegistry).getOrThrow(any());
+ }).when(spyRegistry).getOrThrow((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
}
private static void initFieldDataCache() {
diff --git a/src/test/java/org/bukkit/support/extension/NormalExtension.java b/src/test/java/org/bukkit/support/extension/NormalExtension.java
index 8b5dcc4d0ecf7f9c51f73080c123ca08e31b1898..a809ea2f0d2b477c61857aa02a7e55024b2a7e0d 100644
--- a/src/test/java/org/bukkit/support/extension/NormalExtension.java
+++ b/src/test/java/org/bukkit/support/extension/NormalExtension.java
@@ -62,7 +62,7 @@ public class NormalExtension extends BaseExtension {
doAnswer(invocation ->
mocks.computeIfAbsent(invocation.getArgument(0), k -> mock(RegistryHelper.updateClass(keyed, invocation.getArgument(0)), withSettings().stubOnly().defaultAnswer(DEFAULT_ANSWER)))
- ).when(registry).get(any()); // Allow static classes to fill there fields, so that it does not error out, just by loading them
+ ).when(registry).get((NamespacedKey) any()); // Allow static classes to fill there fields, so that it does not error out, just by loading them // Paper - registry modification api - specifically call namespaced key overload
return registry;
}