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..2fa51c6263 --- /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 #copyOf(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 #copyOf(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 copyOf(TypedKey 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 30f5816550..b4267866bb 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,18 +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.factoryRegister(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 - * builder in the consumer will be pre-filled with the values - * from the copyFrom key. + * {@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 copyFrom the key to copy values from (must already be registered) - * @param value a consumer for the entry's builder - * @throws IllegalArgumentException if copyFrom doesn't exist + * @param value a consumer of a builder factory */ - void register(TypedKey key, TypedKey copyFrom, Consumer value); + void factoryRegister(TypedKey key, Consumer> value); } 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..0f0f8d1c7e --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java @@ -0,0 +1,52 @@ +package io.papermc.paper.registry; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.data.util.Conversions; +import io.papermc.paper.registry.entry.RegistryEntryMeta; +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 RegistryEntryMeta.Buildable meta; + private final Function existingValueGetter; + private @Nullable B builder; + + public PaperRegistryBuilderFactory(final Conversions conversions, final RegistryEntryMeta.Buildable meta, final Function existingValueGetter) { + this.conversions = conversions; + this.meta = meta; + 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.meta.builderFiller().create(this.conversions); + } + + @Override + public B copyOf(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.meta.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 01bfe05e81..42ad4adf47 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 @@ -1,8 +1,6 @@ package io.papermc.paper.registry; -import com.google.common.base.Preconditions; 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.RegistryEntryMeta; import io.papermc.paper.registry.event.WritableRegistry; @@ -13,7 +11,6 @@ import net.minecraft.core.RegistrationInfo; import net.minecraft.resources.ResourceKey; import org.bukkit.Keyed; import org.bukkit.craftbukkit.CraftRegistry; -import org.jspecify.annotations.Nullable; public class WritableCraftRegistry> extends CraftRegistry { @@ -31,23 +28,16 @@ public class WritableCraftRegistry key, final @Nullable TypedKey copyFrom, 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; - if (copyFrom != null) { - final M existing = this.registry.temporaryUnfrozenMap.get(PaperAdventure.asVanilla(copyFrom)); - Preconditions.checkArgument(existing != null, "Cannot copy from unregistered key: %s", copyFrom); - builder = this.meta.builderFiller().fill(conversions, existing); - } else { - builder = this.meta.builderFiller().create(conversions); - } - value.accept(builder); + final PaperRegistryBuilderFactory builderFactory = new PaperRegistryBuilderFactory<>(conversions, this.meta, this.registry.temporaryUnfrozenMap::get); + value.accept(builderFactory); PaperRegistryListenerManager.INSTANCE.registerWithListeners( this.registry, this.meta, resourceKey, - builder, + builderFactory.requireBuilder(), FROM_PLUGIN, conversions ); @@ -66,13 +56,8 @@ public class WritableCraftRegistry key, final Consumer value) { - WritableCraftRegistry.this.register(key, null, value, this.conversions); - } - - @Override - public void register(final TypedKey key, final TypedKey copyFrom, final Consumer value) { - WritableCraftRegistry.this.register(key, copyFrom, value, this.conversions); + public void factoryRegister(final TypedKey key, final Consumer> value) { + WritableCraftRegistry.this.register(key, value, this.conversions); } } }