From af2812fb0f14acea697f3ff6b7b56ffd14900041 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 26 Dec 2024 13:41:15 -0800 Subject: [PATCH] For new registry values, allow copying from existing (#11796) Co-authored-by: Bjarne Koll --- .../registry/RegistryBuilderFactory.java | 39 ++++++++++++++ .../papermc/paper/registry/RegistryKey.java | 24 +++++++++ .../io/papermc/paper/registry/TypedKey.java | 15 ++++++ .../registry/event/WritableRegistry.java | 16 +++++- .../0017-Moonrise-optimisation-patches.patch | 8 +-- .../minecraft/core/MappedRegistry.java.patch | 25 ++++++++- .../registry/PaperRegistryBuilderFactory.java | 51 +++++++++++++++++++ .../paper/registry/WritableCraftRegistry.java | 19 ++----- 8 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java create mode 100644 paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java new file mode 100644 index 0000000000..ce2447d3d6 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java @@ -0,0 +1,39 @@ +package io.papermc.paper.registry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * A factory to create a {@link RegistryBuilder} for a given {@link TypedKey}. For + * each instance of this class, once either {@link #empty()} or {@link #copyFrom(TypedKey)} + * is called once, any future calls to either method will throw an {@link IllegalStateException}. + * + * @param The type of the registry + * @param The type of the registry builder + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface RegistryBuilderFactory> { + + /** + * Creates a new empty {@link RegistryBuilder}. + * + * @return A new empty {@link RegistryBuilder} + * @throws IllegalStateException if this method or {@link #copyFrom(TypedKey)}) has already been called once + */ + @Contract("-> new") + B empty(); + + /** + * Creates a new {@link RegistryBuilder} with the same properties as the given {@link TypedKey}. + * + * @param key The key to copy properties from + * @return A new {@link RegistryBuilder} with the same properties as the given key + * @throws IllegalStateException if this method or {@link #empty()} has already been called once + * @throws IllegalArgumentException if key doesn't exist + */ + @Contract("_ -> new") + B copyFrom(TypedKey key); +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index e5319bdb9f..ea795de955 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -1,6 +1,8 @@ package io.papermc.paper.registry; import io.papermc.paper.datacomponent.DataComponentType; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.key.Keyed; import org.bukkit.Art; import org.bukkit.Fluid; @@ -200,4 +202,26 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { RegistryKey PARTICLE_TYPE = create("particle_type"); RegistryKey POTION = create("potion"); RegistryKey> MEMORY_MODULE_TYPE = create("memory_module_type"); + + /** + * Constructs a new {@link TypedKey} for this registry given the typed key's key. + * + * @param key the key of the typed key. + * @return the constructed typed key. + */ + @ApiStatus.Experimental + default TypedKey typedKey(final Key key) { + return TypedKey.create(this, key); + } + + /** + * Constructs a new {@link TypedKey} for this registry given the typed key's key. + * + * @param key the string representation of the key that will be passed to {@link Key#key(String)}. + * @return the constructed typed key. + */ + @ApiStatus.Experimental + default TypedKey typedKey(final @KeyPattern String key) { + return TypedKey.create(this, key); + } } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java b/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java index 81bee52241..c8f363a240 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java @@ -1,6 +1,7 @@ package io.papermc.paper.registry; import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.key.Keyed; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; @@ -42,4 +43,18 @@ public sealed interface TypedKey extends Key permits TypedKeyImpl { static TypedKey create(final RegistryKey registryKey, final Key key) { return new TypedKeyImpl<>(key, registryKey); } + + /** + * Create a typed key from a string and a registry key. + * + * @param registryKey the registry this key is for + * @param key the string version of a {@link Key} that will be passed to {@link Key#key(String)} for parsing. + * @param value type + * @return a new key for the value key and registry key + * @see Key#key(String) + */ + @ApiStatus.Experimental + static TypedKey create(final RegistryKey registryKey, final @KeyPattern String key) { + return create(registryKey, Key.key(key)); + } } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java b/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java index 744f455b14..fed7ad660f 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java @@ -1,6 +1,7 @@ package io.papermc.paper.registry.event; import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.RegistryBuilderFactory; import io.papermc.paper.registry.TypedKey; import java.util.function.Consumer; import org.jetbrains.annotations.ApiStatus; @@ -24,5 +25,18 @@ public interface WritableRegistry> { * @param key the entry's key (must be unique from others) * @param value a consumer for the entry's builder */ - void register(TypedKey key, Consumer value); + default void register(final TypedKey key, final Consumer value) { + this.registerWith(key, factory -> value.accept(factory.empty())); + } + + /** + * Register a new value with the specified key. This will + * fire a {@link RegistryEntryAddEvent} for the new entry. The + * {@link RegistryBuilderFactory} lets you pre-fill a builder with + * an already-existing entry's properties. + * + * @param key the entry's key (must be unique from others) + * @param value a consumer of a builder factory + */ + void registerWith(TypedKey key, Consumer> value); } diff --git a/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch index 337302aaa2..45b4a15fe7 100644 --- a/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch @@ -23508,10 +23508,10 @@ index 3d3eec1db91cb47395f40c4f47aa77164ad42175..216f97207dac88cc1dc3df59c6ee8a62 + // Paper end - optimise collisions } diff --git a/net/minecraft/core/MappedRegistry.java b/net/minecraft/core/MappedRegistry.java -index 47b1fafd91b39e73c4e9134b0b8048000fba108a..76994c1491221c06cca5405ba239e6ff642b19ed 100644 +index 452c358c2cfa0c39e0b09853cd4a9a12c6ced65d..5f752603aa5611ce9d3dd44cc5b70c27ac46a86e 100644 --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -50,6 +50,19 @@ public class MappedRegistry implements WritableRegistry { +@@ -51,6 +51,19 @@ public class MappedRegistry implements WritableRegistry { return this.getTags(); } @@ -23531,10 +23531,10 @@ index 47b1fafd91b39e73c4e9134b0b8048000fba108a..76994c1491221c06cca5405ba239e6ff public MappedRegistry(ResourceKey> key, Lifecycle registryLifecycle) { this(key, registryLifecycle, false); } -@@ -114,6 +127,7 @@ public class MappedRegistry implements WritableRegistry { - this.toId.put(value, size); +@@ -116,6 +129,7 @@ public class MappedRegistry implements WritableRegistry { this.registrationInfos.put(key, registrationInfo); this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle()); + this.temporaryUnfrozenMap.put(key.location(), value); // Paper - support pre-filling in registry mod API + this.injectFluidRegister(key, value); // Paper - fluid method optimisations return reference; } diff --git a/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch b/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch index 400d714abe..2a92380a2f 100644 --- a/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -33,11 +_,11 @@ +@@ -33,17 +_,18 @@ public class MappedRegistry implements WritableRegistry { private final ResourceKey> key; private final ObjectList> byId = new ObjectArrayList<>(256); @@ -17,6 +17,29 @@ private Lifecycle registryLifecycle; private final Map, HolderSet.Named> frozenTags = new IdentityHashMap<>(); MappedRegistry.TagSet allTags = MappedRegistry.TagSet.unbound(); + private boolean frozen; + @Nullable + private Map> unregisteredIntrusiveHolders; ++ public final Map temporaryUnfrozenMap = new HashMap<>(); // Paper - support pre-filling in registry mod API + + @Override + public Stream> listTags() { +@@ -114,6 +_,7 @@ + this.toId.put(value, size); + this.registrationInfos.put(key, registrationInfo); + this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle()); ++ this.temporaryUnfrozenMap.put(key.location(), value); // Paper - support pre-filling in registry mod API + return reference; + } + } +@@ -275,6 +_,7 @@ + return this; + } else { + this.frozen = true; ++ this.temporaryUnfrozenMap.clear(); // Paper - support pre-filling in registry mod API + this.byValue.forEach((object, reference) -> reference.bindValue((T)object)); + List list = this.byKey + .entrySet() @@ -509,4 +_,13 @@ Stream> getTags(); diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java new file mode 100644 index 0000000000..e83a6336bb --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java @@ -0,0 +1,51 @@ +package io.papermc.paper.registry; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.data.util.Conversions; +import java.util.function.Function; +import net.minecraft.resources.ResourceLocation; +import org.bukkit.Keyed; +import org.jspecify.annotations.Nullable; + +public class PaperRegistryBuilderFactory> implements RegistryBuilderFactory { // TODO remove Keyed + + private final Conversions conversions; + private final PaperRegistryBuilder.Filler builderFiller; + private final Function existingValueGetter; + private @Nullable B builder; + + public PaperRegistryBuilderFactory(final Conversions conversions, final PaperRegistryBuilder.Filler builderFiller, final Function existingValueGetter) { + this.conversions = conversions; + this.builderFiller = builderFiller; + this.existingValueGetter = existingValueGetter; + } + + private void validate() { + if (this.builder != null) { + throw new IllegalStateException("Already created a builder"); + } + } + + public B requireBuilder() { + if (this.builder == null) { + throw new IllegalStateException("Builder not created yet"); + } + return this.builder; + } + + @Override + public B empty() { + this.validate(); + return this.builder = this.builderFiller.create(this.conversions); + } + + @Override + public B copyFrom(final TypedKey key) { + this.validate(); + final M existing = this.existingValueGetter.apply(PaperAdventure.asVanilla(key)); + if (existing == null) { + throw new IllegalArgumentException("Key " + key + " doesn't exist"); + } + return this.builder = this.builderFiller.fill(this.conversions, existing); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java b/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java index 6c17623e76..a294ec37b6 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java @@ -2,20 +2,15 @@ package io.papermc.paper.registry; import com.mojang.serialization.Lifecycle; import io.papermc.paper.registry.data.util.Conversions; -import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; -import io.papermc.paper.registry.entry.RegistryTypeMapper; 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; public class WritableCraftRegistry> extends CraftRegistry { @@ -33,16 +28,16 @@ public class WritableCraftRegistry key, final Consumer value, final Conversions conversions) { + public void register(final TypedKey key, final Consumer> value, final Conversions conversions) { final ResourceKey resourceKey = PaperRegistries.toNms(key); this.registry.validateWrite(resourceKey); - final B builder = this.newBuilder(conversions); - value.accept(builder); + final PaperRegistryBuilderFactory builderFactory = new PaperRegistryBuilderFactory<>(conversions, this.meta.builderFiller(), this.registry.temporaryUnfrozenMap::get); + value.accept(builderFactory); PaperRegistryListenerManager.INSTANCE.registerWithListeners( this.registry, this.meta, resourceKey, - builder, + builderFactory.requireBuilder(), FROM_PLUGIN, conversions ); @@ -52,10 +47,6 @@ public class WritableCraftRegistry { private final Conversions conversions; @@ -65,7 +56,7 @@ public class WritableCraftRegistry key, final Consumer value) { + public void registerWith(final TypedKey key, final Consumer> value) { WritableCraftRegistry.this.register(key, value, this.conversions); } }