diff --git a/paper-api/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java b/paper-api/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java index 3e29f7007500..a4063783c318 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java @@ -1,16 +1,19 @@ package io.papermc.paper.registry; import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import org.jspecify.annotations.NullMarked; @NullMarked record TypedKeyImpl<T>(Key key, RegistryKey<T> registryKey) implements TypedKey<T> { // Wrap key methods to make this easier to use + @KeyPattern.Namespace @Override public String namespace() { return this.key.namespace(); } + @KeyPattern.Value @Override public String value() { return this.key.value(); diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/JukeboxSongRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/JukeboxSongRegistryEntry.java new file mode 100644 index 000000000000..468cfff4f43a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/JukeboxSongRegistryEntry.java @@ -0,0 +1,122 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.RegistryBuilderFactory; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.util.Either; +import java.util.function.Consumer; +import net.kyori.adventure.text.Component; +import org.bukkit.JukeboxSong; +import org.bukkit.Sound; +import org.checkerframework.checker.index.qual.Positive; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Range; + +/** + * A data-centric version-specific registry entry for the {@link JukeboxSong} type. + */ +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface JukeboxSongRegistryEntry { + + /** + * Gets the sound event for this song. + * + * @return the sound event + */ + @Contract(pure = true) + Either<TypedKey<Sound>, SoundEventRegistryEntry> soundEvent(); + + /** + * Gets the description for this song. + * + * @return the description + */ + @Contract(pure = true) + Component description(); + + /** + * Gets the length in seconds for this song. + * + * @return the length in seconds + */ + @Contract(pure = true) + @Positive float lengthInSeconds(); + + /** + * Gets the comparator output for this song. + * + * @return the comparator output + */ + @Contract(pure = true) + @Range(from = 0, to = 15) int comparatorOutput(); + + /** + * A mutable builder for the {@link JukeboxSongRegistryEntry} plugins may change in applicable registry events. + * <p> + * The following values are required for each builder: + * <ul> + * <li> + * {@link #soundEvent(TypedKey)} or {@link #soundEvent(Consumer)} + * </li> + * <li>{@link #description(Component)}</li> + * <li>{@link #lengthInSeconds(float)}</li> + * <li>{@link #comparatorOutput(int)}</li> + * </ul> + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends JukeboxSongRegistryEntry, RegistryBuilder<JukeboxSong> { + + /** + * Sets the sound event for this song to a sound event present + * in the {@link io.papermc.paper.registry.RegistryKey#SOUND_EVENT} registry. + * <p>This will override {@link #soundEvent(Consumer)}</p> + * + * @param soundEvent the sound event + * @return this builder + * @see #soundEvent(Consumer) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder soundEvent(TypedKey<Sound> soundEvent); + + /** + * Sets the sound event for this song to a new sound event. + * <p>This will override {@link #soundEvent(TypedKey)}</p> + * + * @param soundEvent the sound event + * @return this builder + * @see #soundEvent(TypedKey) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder soundEvent(Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> soundEvent); + + /** + * Sets the description for this song. + * + * @param description the description + * @return this builder + */ + @Contract(value = "_ -> this", mutates = "this") + Builder description(Component description); + + /** + * Sets the length in seconds for this song. + * + * @param lengthInSeconds the length in seconds (positive) + * @return this builder + */ + @Contract(value = "_ -> this", mutates = "this") + Builder lengthInSeconds(@Positive float lengthInSeconds); + + /** + * Sets the comparator output for this song. + * + * @param comparatorOutput the comparator output [0-15] + * @return this builder + */ + @Contract(value = "_ -> this", mutates = "this") + Builder comparatorOutput(@Range(from = 0, to = 15) int comparatorOutput); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/SoundEventRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/SoundEventRegistryEntry.java new file mode 100644 index 000000000000..ea68f71a6920 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/SoundEventRegistryEntry.java @@ -0,0 +1,63 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import net.kyori.adventure.key.Key; +import org.bukkit.Sound; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.Nullable; + +/** + * A data-centric version-specific registry entry for the {@link Sound} type. + */ +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface SoundEventRegistryEntry { + + /** + * Gets the resource pack location for this sound event. + * + * @return the location + */ + @Contract(pure = true) + Key location(); + + /** + * Gets the fixed range for this sound event, if present. + * + * @return the fixed range, or {@code null} if not present + */ + @Contract(pure = true) + @Nullable Float fixedRange(); + + /** + * A mutable builder for the {@link SoundEventRegistryEntry} plugins may change in applicable registry events. + * <p> + * The following values are required for each builder: + * <ul> + * <li>{@link #location(Key)}</li> + * </ul> + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends SoundEventRegistryEntry, RegistryBuilder<Sound> { + + /** + * Sets the resource pack location for this sound event. + * + * @param location the location + * @return this builder + */ + @Contract(value = "_ -> this", mutates = "this") + Builder location(Key location); + + /** + * Sets the fixed range for this sound event. + * + * @param fixedRange the fixed range + * @return this builder + */ + @Contract(value = "_ -> this", mutates = "this") + Builder fixedRange(@Nullable Float fixedRange); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java index d15581579445..79e898a3ba1e 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -5,9 +5,11 @@ import io.papermc.paper.registry.data.DamageTypeRegistryEntry; import io.papermc.paper.registry.data.EnchantmentRegistryEntry; import io.papermc.paper.registry.data.GameEventRegistryEntry; +import io.papermc.paper.registry.data.JukeboxSongRegistryEntry; import io.papermc.paper.registry.data.PaintingVariantRegistryEntry; import org.bukkit.Art; import org.bukkit.GameEvent; +import org.bukkit.JukeboxSong; import org.bukkit.block.banner.PatternType; import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; @@ -29,6 +31,7 @@ public final class RegistryEvents { public static final RegistryEventProvider<Art, PaintingVariantRegistryEntry.Builder> PAINTING_VARIANT = create(RegistryKey.PAINTING_VARIANT); public static final RegistryEventProvider<PatternType, BannerPatternRegistryEntry.Builder> BANNER_PATTERN = create(RegistryKey.BANNER_PATTERN); public static final RegistryEventProvider<DamageType, DamageTypeRegistryEntry.Builder> DAMAGE_TYPE = create(RegistryKey.DAMAGE_TYPE); + public static final RegistryEventProvider<JukeboxSong, JukeboxSongRegistryEntry.Builder> JUKEBOX_SONG = create(RegistryKey.JUKEBOX_SONG); private RegistryEvents() { } diff --git a/paper-api/src/main/java/io/papermc/paper/util/Either.java b/paper-api/src/main/java/io/papermc/paper/util/Either.java new file mode 100644 index 000000000000..013af656bba4 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/util/Either.java @@ -0,0 +1,109 @@ +package io.papermc.paper.util; + +import java.util.Optional; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * A type that can be either a left value or a right value. + * + * @param <L> the type of the left value + * @param <R> the type of the right value + */ +@NullMarked +public sealed interface Either<L, R> permits Either.Left, Either.Right { + + /** + * Create a new Either with a left value. + * + * @param value the left value + * @return a new Either with a left value + * @param <L> the type of the left value + * @param <R> the type of the right value + */ + @Contract(value = "_ -> new", pure = true) + static <L, R> Either.Left<L, R> left(final L value) { + return new EitherLeft<>(value); + } + + /** + * Create a new Either with a right value. + * + * @param value the right value + * @return a new Either with a right value + * @param <L> the type of the left value + * @param <R> the type of the right value + */ + @Contract(value = "_ -> new", pure = true) + static <L, R> Either.Right<L, R> right(final R value) { + return new EitherRight<>(value); + } + + /** + * Get an optional of the left value. + * + * @return an optional of the left value + */ + Optional<L> left(); + + /** + * Get an optional of the right value. + * + * @return an optional of the right value + */ + Optional<R> right(); + + /** + * A left value. + * + * @param <L> the type of the left value + * @param <R> the type of the right value + */ + sealed interface Left<L, R> extends Either<L, R> permits EitherLeft { + + /** + * Get the left value. + * + * @return the left value + */ + @Contract(pure = true) + L value(); + + @Override + default Optional<L> left() { + return Optional.of(this.value()); + } + + @Override + default Optional<R> right() { + return Optional.empty(); + } + } + + /** + * A right value. + * + * @param <L> the type of the left value + * @param <R> the type of the right value + */ + sealed interface Right<L, R> extends Either<L, R> permits EitherRight { + + /** + * Get the right value. + * + * @return the right value + */ + @Contract(pure = true) + R value(); + + @Override + default Optional<L> left() { + return Optional.empty(); + } + + @Override + default Optional<R> right() { + return Optional.of(this.value()); + } + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/util/EitherLeft.java b/paper-api/src/main/java/io/papermc/paper/util/EitherLeft.java new file mode 100644 index 000000000000..a5bb921158e6 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/util/EitherLeft.java @@ -0,0 +1,9 @@ +package io.papermc.paper.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@ApiStatus.Internal +@NullMarked +record EitherLeft<L, R>(L value) implements Either.Left<L, R> { +} diff --git a/paper-api/src/main/java/io/papermc/paper/util/EitherRight.java b/paper-api/src/main/java/io/papermc/paper/util/EitherRight.java new file mode 100644 index 000000000000..b13197f8ed3a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/util/EitherRight.java @@ -0,0 +1,9 @@ +package io.papermc.paper.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +@ApiStatus.Internal +record EitherRight<L, R>(R value) implements Either.Right<L, R> { +} diff --git a/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch b/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch index ef5f7da45fe9..d5bf5cbb2c73 100644 --- a/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch +++ b/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch @@ -29346,10 +29346,10 @@ index 0000000000000000000000000000000000000000..62c0f4073aff301bf5b3187e0d4446fd +} diff --git a/ca/spottedleaf/dataconverter/util/CommandArgumentUpgrader.java b/ca/spottedleaf/dataconverter/util/CommandArgumentUpgrader.java new file mode 100644 -index 0000000000000000000000000000000000000000..40da70d5cf584a9730f9fe81c355cf8513fba475 +index 0000000000000000000000000000000000000000..9fab2371790596ab57298fe28b8983d85210c6d9 --- /dev/null +++ b/ca/spottedleaf/dataconverter/util/CommandArgumentUpgrader.java -@@ -0,0 +1,592 @@ +@@ -0,0 +1,597 @@ +package ca.spottedleaf.dataconverter.util; + +import ca.spottedleaf.dataconverter.minecraft.MCDataConverter; @@ -29908,6 +29908,11 @@ index 0000000000000000000000000000000000000000..40da70d5cf584a9730f9fe81c355cf85 + ) { + return Optional.of(new HolderLookup.RegistryLookup<T>() { + @Override ++ public Optional<T> getValueForCopying(final ResourceKey<T> resourceKey) { ++ return Optional.empty(); ++ } ++ ++ @Override + public ResourceKey<? extends Registry<? extends T>> key() { + return registryRef; + } @@ -30621,7 +30626,7 @@ index 1110ca4075a1bbaa46b66686435dab91b275c945..c2218630c3074c8b3f82364e37503b12 return structureTemplate.save(new CompoundTag()); } diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 0d65bf24f515b80701150fdc430f324a533cb478..b92a3da5c325e69f5601416d4205fb33429742b3 100644 +index 4b9df7d4aab3f7dae2d3b8ced4663162334cdbe6..5ef7a016a0c7bc09d5cd24f14124b15b99d5e18d 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa diff --git a/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch index 6d5960f460d9..8c53a457b0a0 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 452c358c2cfa0c39e0b09853cd4a9a12c6ced65d..5f752603aa5611ce9d3dd44cc5b70c27ac46a86e 100644 +index 450a5d78d2b1885bc5c2db7e969f87a3db440bf0..5d560486d0765d3f1bc1d51ae6f7f676e8f2383f 100644 --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -51,6 +51,19 @@ public class MappedRegistry<T> implements WritableRegistry<T> { +@@ -58,6 +58,19 @@ public class MappedRegistry<T> implements WritableRegistry<T> { return this.getTags(); } @@ -23531,7 +23531,7 @@ index 452c358c2cfa0c39e0b09853cd4a9a12c6ced65d..5f752603aa5611ce9d3dd44cc5b70c27 public MappedRegistry(ResourceKey<? extends Registry<T>> key, Lifecycle registryLifecycle) { this(key, registryLifecycle, false); } -@@ -116,6 +129,7 @@ public class MappedRegistry<T> implements WritableRegistry<T> { +@@ -123,6 +136,7 @@ public class MappedRegistry<T> implements WritableRegistry<T> { 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 @@ -23552,7 +23552,7 @@ index 2b46ca9a2a046063cad422bec00d76107537b091..9aa664537cc37e44db46d5a2a64ae311 thread1 -> { DedicatedServer dedicatedServer1 = new DedicatedServer( diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 841a41485af62470d833aba578069b19a0bd1e8d..409c1134327bfcc338c3ac5e658a83cc396645d1 100644 +index 5ef7a016a0c7bc09d5cd24f14124b15b99d5e18d..2769b3cb17b8101dd90fb95d3853d6083fd0c3e2 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2; diff --git a/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch b/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch new file mode 100644 index 000000000000..17edc3610ce4 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch @@ -0,0 +1,40 @@ +--- a/net/minecraft/core/HolderLookup.java ++++ b/net/minecraft/core/HolderLookup.java +@@ -68,6 +_,9 @@ + } + + public interface RegistryLookup<T> extends HolderLookup<T>, HolderOwner<T> { ++ ++ Optional<T> getValueForCopying(ResourceKey<T> resourceKey); // Paper - add method to get the value for pre-filling builders in the reg mod API ++ + ResourceKey<? extends Registry<? extends T>> key(); + + Lifecycle registryLifecycle(); +@@ -80,6 +_,13 @@ + + default HolderLookup.RegistryLookup<T> filterElements(final Predicate<T> predicate) { + return new HolderLookup.RegistryLookup.Delegate<T>() { ++ // Paper start - add getValueForCopying ++ @Override ++ public Optional<T> getValueForCopying(final ResourceKey<T> resourceKey) { ++ return this.parent().getValueForCopying(resourceKey).filter(predicate); ++ } ++ // Paper end - add getValueForCopying ++ + @Override + public HolderLookup.RegistryLookup<T> parent() { + return RegistryLookup.this; +@@ -99,6 +_,13 @@ + + public interface Delegate<T> extends HolderLookup.RegistryLookup<T> { + HolderLookup.RegistryLookup<T> parent(); ++ ++ // Paper start - add getValueForCopying ++ @Override ++ default Optional<T> getValueForCopying(ResourceKey<T> resourceKey) { ++ return this.parent().getValueForCopying(resourceKey); ++ } ++ // Paper end - add getValueForCopying + + @Override + default ResourceKey<? extends Registry<? extends T>> key() { 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 2a92380a2fa7..975ecbecd0e9 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,17 +_,18 @@ +@@ -33,17 +_,25 @@ public class MappedRegistry<T> implements WritableRegistry<T> { private final ResourceKey<? extends Registry<T>> key; private final ObjectList<Holder.Reference<T>> byId = new ObjectArrayList<>(256); @@ -20,7 +20,14 @@ private boolean frozen; @Nullable private Map<T, Holder.Reference<T>> unregisteredIntrusiveHolders; -+ public final Map<ResourceLocation, T> temporaryUnfrozenMap = new HashMap<>(); // Paper - support pre-filling in registry mod API ++ // Paper start - support pre-filling in registry mod API ++ private final Map<ResourceLocation, T> temporaryUnfrozenMap = new HashMap<>(); ++ ++ @Override ++ public Optional<T> getValueForCopying(final ResourceKey<T> resourceKey) { ++ return this.frozen ? this.getOptional(resourceKey) : Optional.ofNullable(this.temporaryUnfrozenMap.get(resourceKey.location())); ++ } ++ // Paper end - support pre-filling in registry mod API @Override public Stream<HolderSet.Named<T>> listTags() { diff --git a/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch b/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch new file mode 100644 index 000000000000..150d368bed7f --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch @@ -0,0 +1,30 @@ +--- a/net/minecraft/core/RegistrySetBuilder.java ++++ b/net/minecraft/core/RegistrySetBuilder.java +@@ -41,6 +_,13 @@ + final Map<ResourceKey<T>, Holder.Reference<T>> elements + ) { + return new RegistrySetBuilder.EmptyTagRegistryLookup<T>(owner) { ++ // Paper start - add getValueForCopying method ++ @Override ++ public Optional<T> getValueForCopying(final ResourceKey<T> resourceKey) { ++ return this.get(resourceKey).map(Holder.Reference::value); ++ } ++ // Paper end - add getValueForCopying method ++ + @Override + public ResourceKey<? extends Registry<? extends T>> key() { + return registryKey; +@@ -121,6 +_,13 @@ + public <T> Optional<RegistryOps.RegistryInfo<T>> lookup(ResourceKey<? extends Registry<? extends T>> registryKey) { + return getEntry(registryKey).map(Entry::opsInfo); + } ++ ++ // Paper start - add method to get the value for pre-filling builders in the reg mod API ++ @Override ++ public HolderLookup.Provider lookupForValueCopyViaBuilders() { ++ throw new UnsupportedOperationException("Not implemented"); ++ } ++ // Paper end - add method to get the value for pre-filling builders in the reg mod API + }); + } + }; diff --git a/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch b/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch index 36c52e0a7a35..809ce62eb670 100644 --- a/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch @@ -1,19 +1,13 @@ --- a/net/minecraft/core/registries/BuiltInRegistries.java +++ b/net/minecraft/core/registries/BuiltInRegistries.java -@@ -296,6 +_,17 @@ +@@ -296,6 +_,11 @@ 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()) -+ ); -+ } -+ }); ++ public static final io.papermc.paper.registry.data.util.Conversions STATIC_ACCESS_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions( ++ new net.minecraft.resources.RegistryOps.HolderLookupAdapter(net.minecraft.server.RegistryLayer.STATIC_ACCESS) ++ ); + // Paper end - add built-in registry conversions private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> key, BuiltInRegistries.RegistryBootstrap<T> bootstrap) { @@ -65,7 +59,7 @@ for (Registry<?> registry : REGISTRY) { bindBootstrappedTagsToEmpty(registry); -+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BUILT_IN_CONVERSIONS); // Paper ++ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), STATIC_ACCESS_CONVERSIONS); // Paper registry.freeze(); } } diff --git a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch index 97a14239e606..9e6ac9823ffb 100644 --- a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch @@ -1,5 +1,27 @@ --- a/net/minecraft/resources/RegistryDataLoader.java +++ b/net/minecraft/resources/RegistryDataLoader.java +@@ -179,11 +_,21 @@ + final Map<ResourceKey<? extends Registry<?>>, RegistryOps.RegistryInfo<?>> map = new HashMap<>(); + registryLookups.forEach(registryLookup -> map.put(registryLookup.key(), createInfoForContextRegistry((HolderLookup.RegistryLookup<?>)registryLookup))); + loaders.forEach(loader -> map.put(loader.registry.key(), createInfoForNewRegistry(loader.registry))); ++ // this creates a HolderLookup.Provider to be used exclusively to obtain real instances of values to pre-populate builders ++ // for the Registry Modification API. This concatenates the context registry lookups and the empty registries about to be filled. ++ final HolderLookup.Provider providerForBuilders = HolderLookup.Provider.create(java.util.stream.Stream.concat(registryLookups.stream(), loaders.stream().map(Loader::registry))); // Paper - add method to get the value for pre-filling builders in the reg mod API + return new RegistryOps.RegistryInfoLookup() { + @Override + public <T> Optional<RegistryOps.RegistryInfo<T>> lookup(ResourceKey<? extends Registry<? extends T>> registryKey) { + return Optional.ofNullable((RegistryOps.RegistryInfo<T>)map.get(registryKey)); + } ++ ++ // Paper start - add method to get the value for pre-filling builders in the reg mod API ++ @Override ++ public HolderLookup.Provider lookupForValueCopyViaBuilders() { ++ return providerForBuilders; ++ } ++ // Paper end - add method to get the value for pre-filling builders in the reg mod API + }; + } + @@ -247,13 +_,14 @@ RegistryOps<JsonElement> ops, ResourceKey<E> resourceKey, diff --git a/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch new file mode 100644 index 000000000000..93b5b31807ba --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch @@ -0,0 +1,24 @@ +--- a/net/minecraft/resources/RegistryOps.java ++++ b/net/minecraft/resources/RegistryOps.java +@@ -117,6 +_,13 @@ + public int hashCode() { + return this.lookupProvider.hashCode(); + } ++ ++ // Paper start - add method to get the value for pre-filling builders in the reg mod API ++ @Override ++ public HolderLookup.Provider lookupForValueCopyViaBuilders() { ++ return this.lookupProvider; ++ } ++ // Paper end - add method to get the value for pre-filling builders in the reg mod API + } + + public record RegistryInfo<T>(HolderOwner<T> owner, HolderGetter<T> getter, Lifecycle elementsLifecycle) { +@@ -127,5 +_,7 @@ + + public interface RegistryInfoLookup { + <T> Optional<RegistryOps.RegistryInfo<T>> lookup(ResourceKey<? extends Registry<? extends T>> registryKey); ++ ++ HolderLookup.Provider lookupForValueCopyViaBuilders(); // Paper - add method to get the value for pre-filling builders in the reg mod API + } + } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java index 15c66b0186ff..4164e35c528b 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java @@ -3,13 +3,13 @@ import com.destroystokyo.paper.profile.PlayerProfile; import com.google.common.base.Preconditions; import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; import io.papermc.paper.registry.set.RegistryKeySet; import io.papermc.paper.registry.tag.TagKey; import io.papermc.paper.text.Filtered; import net.kyori.adventure.key.Key; import net.kyori.adventure.util.TriState; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.component.OminousBottleAmplifier; import org.bukkit.JukeboxSong; @@ -208,7 +208,7 @@ public Enchantable enchantable(final int level) { @Override public Repairable repairable(final RegistryKeySet<ItemType> types) { return new PaperRepairable(new net.minecraft.world.item.enchantment.Repairable( - PaperRegistrySets.convertToNms(Registries.ITEM, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), types) + PaperRegistrySets.convertToNms(Registries.ITEM, Conversions.global().lookup(), types) )); } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java index 6d427d2c62cc..237d42f45f31 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java @@ -1,22 +1,19 @@ package io.papermc.paper.datacomponent.item; import io.papermc.paper.adventure.PaperAdventure; -import io.papermc.paper.registry.PaperRegistries; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; import io.papermc.paper.registry.set.RegistryKeySet; import java.util.Optional; -import java.util.function.Function; import net.kyori.adventure.key.Key; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; -import net.minecraft.util.datafix.fixes.EquippableAssetRenameFix; import net.minecraft.world.item.equipment.EquipmentAsset; import net.minecraft.world.item.equipment.EquipmentAssets; import org.bukkit.craftbukkit.CraftEquipmentSlot; @@ -133,7 +130,7 @@ public Builder cameraOverlay(@Nullable final Key cameraOverlay) { @Override public Builder allowedEntities(final @Nullable RegistryKeySet<EntityType> allowedEntities) { this.allowedEntities = Optional.ofNullable(allowedEntities) - .map((set) -> PaperRegistrySets.convertToNms(Registries.ENTITY_TYPE, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), set)); + .map((set) -> PaperRegistrySets.convertToNms(Registries.ENTITY_TYPE, Conversions.global().lookup(), set)); return this; } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java index e6315cd0ebd4..238b1b581b27 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java @@ -2,12 +2,12 @@ import io.papermc.paper.block.BlockPredicate; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; import io.papermc.paper.util.MCUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Optional; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import org.bukkit.craftbukkit.util.Handleable; @@ -48,7 +48,7 @@ static final class BuilderImpl implements ItemAdventurePredicate.Builder { @Override public ItemAdventurePredicate.Builder addPredicate(final BlockPredicate predicate) { this.predicates.add(new net.minecraft.advancements.critereon.BlockPredicate(Optional.ofNullable(predicate.blocks()).map( - blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), blocks) + blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, Conversions.global().lookup(), blocks) ), Optional.empty(), Optional.empty())); return this; } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java index 538a61eaa02c..66918e1d69bd 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java @@ -2,6 +2,7 @@ import com.google.common.base.Preconditions; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; import io.papermc.paper.registry.set.RegistryKeySet; import io.papermc.paper.util.MCUtil; @@ -10,7 +11,6 @@ import java.util.List; import java.util.Optional; import net.kyori.adventure.util.TriState; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import org.bukkit.block.BlockType; import org.bukkit.craftbukkit.util.Handleable; @@ -79,7 +79,7 @@ public Builder defaultMiningSpeed(final float miningSpeed) { @Override public Builder addRule(final Rule rule) { this.rules.add(new net.minecraft.world.item.component.Tool.Rule( - PaperRegistrySets.convertToNms(Registries.BLOCK, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), rule.blocks()), + PaperRegistrySets.convertToNms(Registries.BLOCK, Conversions.global().lookup(), rule.blocks()), Optional.ofNullable(rule.speed()), Optional.ofNullable(rule.correctForDrops().toBoolean()) )); diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java index eab1883d691e..91c68219d6d6 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java @@ -3,12 +3,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; import io.papermc.paper.registry.set.RegistryKeySet; import java.util.ArrayList; import java.util.List; import net.kyori.adventure.key.Key; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import org.bukkit.craftbukkit.potion.CraftPotionUtil; import org.bukkit.potion.PotionEffect; @@ -35,7 +35,7 @@ public ConsumeEffect.ApplyStatusEffects applyStatusEffects(final List<PotionEffe public ConsumeEffect.RemoveStatusEffects removeStatusEffects(final RegistryKeySet<PotionEffectType> effectTypes) { return new PaperRemoveStatusEffects( new net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect( - PaperRegistrySets.convertToNms(Registries.MOB_EFFECT, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), effectTypes) + PaperRegistrySets.convertToNms(Registries.MOB_EFFECT, Conversions.global().lookup(), effectTypes) ) ); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java index f6e6aeee15c8..acb79d216de2 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -8,7 +8,9 @@ import io.papermc.paper.registry.data.PaperDamageTypeRegistryEntry; import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry; import io.papermc.paper.registry.data.PaperGameEventRegistryEntry; +import io.papermc.paper.registry.data.PaperJukeboxSongRegistryEntry; import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry; +import io.papermc.paper.registry.data.PaperSoundEventRegistryEntry; import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; import io.papermc.paper.registry.tag.TagKey; @@ -97,7 +99,9 @@ public final class PaperRegistries { start(Registries.MENU, RegistryKey.MENU).craft(MenuType.class, CraftMenuType::new).build(), start(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE).craft(Attribute.class, CraftAttribute::new).build(), start(Registries.FLUID, RegistryKey.FLUID).craft(Fluid.class, CraftFluid::new).build(), - start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new).build(), + start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT) + .craft(Sound.class, CraftSound::new) + .create(PaperSoundEventRegistryEntry.Builder::new, RegistryEntryMeta.RegistryModificationApiSupport.NONE), start(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE).craft(DataComponentTypes.class, PaperDataComponentType::of).build(), // data-drivens @@ -108,7 +112,7 @@ public final class PaperRegistries { start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).writable(PaperDamageTypeRegistryEntry.PaperBuilder::new).delayed(), start(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT).craft(Wolf.Variant.class, CraftWolf.CraftVariant::new).build().delayed(), start(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT).craft(Enchantment.class, CraftEnchantment::new).serializationUpdater(FieldRename.ENCHANTMENT_RENAME).writable(PaperEnchantmentRegistryEntry.PaperBuilder::new).delayed(), - start(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG).craft(JukeboxSong.class, CraftJukeboxSong::new).build().delayed(), + start(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG).craft(JukeboxSong.class, CraftJukeboxSong::new).writable(PaperJukeboxSongRegistryEntry.Builder::new).delayed(), start(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN).craft(PatternType.class, CraftPatternType::new).writable(PaperBannerPatternRegistryEntry.PaperBuilder::new).delayed(), start(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT).craft(Art.class, CraftArt::new).writable(PaperPaintingVariantRegistryEntry.PaperBuilder::new).delayed(), start(Registries.INSTRUMENT, RegistryKey.INSTRUMENT).craft(MusicInstrument.class, CraftMusicInstrument::new).build().delayed(), 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 index e83a6336bbd1..712b88b42353 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java @@ -2,19 +2,28 @@ import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.registry.data.util.Conversions; +import java.util.Optional; import java.util.function.Function; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; import org.bukkit.Keyed; import org.jspecify.annotations.Nullable; public class PaperRegistryBuilderFactory<M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> implements RegistryBuilderFactory<A, B> { // TODO remove Keyed + private final ResourceKey<? extends Registry<M>> registryKey; private final Conversions conversions; private final PaperRegistryBuilder.Filler<M, A, B> builderFiller; - private final Function<? super ResourceLocation, ? extends @Nullable M> existingValueGetter; + private final Function<ResourceKey<M>, Optional<M>> existingValueGetter; private @Nullable B builder; - public PaperRegistryBuilderFactory(final Conversions conversions, final PaperRegistryBuilder.Filler<M, A, B> builderFiller, final Function<? super ResourceLocation, ? extends @Nullable M> existingValueGetter) { + public PaperRegistryBuilderFactory( + final ResourceKey<? extends Registry<M>> registryKey, + final Conversions conversions, + final PaperRegistryBuilder.Filler<M, A, B> builderFiller, + final Function<ResourceKey<M>, Optional<M>> existingValueGetter + ) { + this.registryKey = registryKey; this.conversions = conversions; this.builderFiller = builderFiller; this.existingValueGetter = existingValueGetter; @@ -42,10 +51,10 @@ public B empty() { @Override public B copyFrom(final TypedKey<A> key) { this.validate(); - final M existing = this.existingValueGetter.apply(PaperAdventure.asVanilla(key)); - if (existing == null) { + final Optional<M> existing = this.existingValueGetter.apply(PaperAdventure.asVanilla(this.registryKey, key)); + if (existing.isEmpty()) { throw new IllegalArgumentException("Key " + key + " doesn't exist"); } - return this.builder = this.builderFiller.fill(this.conversions, existing); + return this.builder = this.builderFiller.fill(this.conversions, existing.get()); } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java index 540aaa09649c..bbec3c83379e 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java @@ -60,7 +60,7 @@ public <M> M registerWithListeners(final Registry<M> registry, final ResourceLoc * 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); + return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, PaperRegistryListenerManager::registerWithInstance, BuiltInRegistries.STATIC_ACCESS_CONVERSIONS); } /** @@ -74,7 +74,7 @@ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> * 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); + return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, WritableRegistry::register, BuiltInRegistries.STATIC_ACCESS_CONVERSIONS); } public <M> void registerWithListeners( 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 a294ec37b6f5..9e8672c705eb 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 @@ -31,7 +31,7 @@ public WritableCraftRegistry( public void register(final TypedKey<T> key, final Consumer<RegistryBuilderFactory<T, B>> value, final Conversions conversions) { final ResourceKey<M> resourceKey = PaperRegistries.toNms(key); this.registry.validateWrite(resourceKey); - final PaperRegistryBuilderFactory<M, T, B> builderFactory = new PaperRegistryBuilderFactory<>(conversions, this.meta.builderFiller(), this.registry.temporaryUnfrozenMap::get); + final PaperRegistryBuilderFactory<M, T, B> builderFactory = new PaperRegistryBuilderFactory<>(this.registry.key(), conversions, this.meta.builderFiller(), this.registry::getValueForCopying); value.accept(builderFactory); PaperRegistryListenerManager.INSTANCE.registerWithListeners( this.registry, diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProviderImpl.java b/paper-server/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProviderImpl.java index 8d7d75840a50..bb74dd085ad5 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProviderImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProviderImpl.java @@ -1,34 +1,15 @@ package io.papermc.paper.registry.data; -import com.google.common.base.Preconditions; -import io.papermc.paper.registry.PaperRegistries; -import io.papermc.paper.registry.PaperRegistryBuilder; -import io.papermc.paper.registry.PaperRegistryBuilderFactory; import io.papermc.paper.registry.RegistryBuilderFactory; import io.papermc.paper.registry.data.util.Conversions; -import io.papermc.paper.registry.entry.RegistryEntryMeta; import java.util.function.Consumer; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import org.bukkit.Art; -import org.bukkit.Keyed; -import org.bukkit.craftbukkit.CraftRegistry; -@SuppressWarnings("BoundedWildcard") public final class InlinedRegistryBuilderProviderImpl implements InlinedRegistryBuilderProvider { - private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A create(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<PaperRegistryBuilderFactory<M, A, B>> value) { - final RegistryEntryMeta.Buildable<M, A, B> buildableMeta = PaperRegistries.getBuildableMeta(registryKey); - Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders"); - final PaperRegistryBuilderFactory<M, A, B> builderFactory = new PaperRegistryBuilderFactory<>(Conversions.global(), buildableMeta.builderFiller(), CraftRegistry.getMinecraftRegistry(buildableMeta.mcKey())::getValue); - value.accept(builderFactory); - return buildableMeta.registryTypeMapper().convertDirectHolder(Holder.direct(builderFactory.requireBuilder().build())); - } - @Override public Art createPaintingVariant(final Consumer<RegistryBuilderFactory<Art, ? extends PaintingVariantRegistryEntry.Builder>> value) { - return create(Registries.PAINTING_VARIANT, value::accept); + return Conversions.global().createApiInstanceFromBuilder(Registries.PAINTING_VARIANT, value); } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperJukeboxSongRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperJukeboxSongRegistryEntry.java new file mode 100644 index 000000000000..21f2486d9116 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperJukeboxSongRegistryEntry.java @@ -0,0 +1,117 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.RegistryBuilderFactory; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.data.util.Conversions; +import io.papermc.paper.util.Either; +import java.util.OptionalInt; +import java.util.function.Consumer; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.JukeboxSong; +import net.minecraft.world.level.redstone.Redstone; +import org.bukkit.Sound; +import org.checkerframework.checker.index.qual.Positive; +import org.jetbrains.annotations.Range; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; +import static io.papermc.paper.registry.data.util.Checks.asArgumentMinExclusive; +import static io.papermc.paper.registry.data.util.Checks.asArgumentRange; +import static io.papermc.paper.registry.data.util.Checks.asConfigured; + +public class PaperJukeboxSongRegistryEntry implements JukeboxSongRegistryEntry { + + protected final Conversions conversions; + protected @Nullable Holder<SoundEvent> soundEvent; + protected @Nullable Component description; + protected @Nullable Float lengthInSeconds; + protected OptionalInt comparatorOutput = OptionalInt.empty(); + + public PaperJukeboxSongRegistryEntry(final Conversions conversions, final @Nullable JukeboxSong internal) { + this.conversions = conversions; + + if (internal == null) { + return; + } + this.soundEvent = internal.soundEvent(); + this.description = internal.description(); + this.lengthInSeconds = internal.lengthInSeconds(); + this.comparatorOutput = OptionalInt.of(internal.comparatorOutput()); + } + + @Override + public Either<TypedKey<Sound>, SoundEventRegistryEntry> soundEvent() { + final Holder<SoundEvent> current = asConfigured(this.soundEvent, "soundEvent"); + return current.unwrap().map( + l -> Either.left(PaperRegistries.fromNms(l)), + r -> Either.right(new PaperSoundEventRegistryEntry(this.conversions, r)) + ); + } + + @Override + public net.kyori.adventure.text.Component description() { + return this.conversions.asAdventure(asConfigured(this.description, "description")); + } + + @Override + public float lengthInSeconds() { + return asConfigured(this.lengthInSeconds, "lengthInSeconds"); + } + + @Override + public int comparatorOutput() { + return asConfigured(this.comparatorOutput, "comparatorOutput"); + } + + public static final class Builder extends PaperJukeboxSongRegistryEntry implements JukeboxSongRegistryEntry.Builder, PaperRegistryBuilder<JukeboxSong, org.bukkit.JukeboxSong> { + + public Builder(final Conversions conversions, final @Nullable JukeboxSong internal) { + super(conversions, internal); + } + + @Override + public JukeboxSongRegistryEntry.Builder soundEvent(final TypedKey<Sound> soundEvent) { + this.soundEvent = this.conversions.getReferenceHolder(PaperRegistries.toNms(asArgument(soundEvent, "soundEvent"))); + return this; + } + + @Override + public JukeboxSongRegistryEntry.Builder soundEvent(final Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> soundEvent) { + this.soundEvent = this.conversions.createHolderFromBuilder(Registries.SOUND_EVENT, asArgument(soundEvent, "soundEvent")); + return this; + } + + @Override + public JukeboxSongRegistryEntry.Builder description(final net.kyori.adventure.text.Component description) { + this.description = this.conversions.asVanilla(asArgument(description, "description")); + return this; + } + + @Override + public JukeboxSongRegistryEntry.Builder lengthInSeconds(final @Positive float lengthInSeconds) { + this.lengthInSeconds = asArgumentMinExclusive(lengthInSeconds, "lengthInSeconds", 0); + return this; + } + + @Override + public JukeboxSongRegistryEntry.Builder comparatorOutput(final @Range(from = 0, to = 15) int comparatorOutput) { + this.comparatorOutput = OptionalInt.of(asArgumentRange(comparatorOutput, "comparatorOutput", Redstone.SIGNAL_MIN, Redstone.SIGNAL_MAX)); + return this; + } + + @Override + public JukeboxSong build() { + return new JukeboxSong( + asConfigured(this.soundEvent, "soundEvent"), + asConfigured(this.description, "description"), + this.lengthInSeconds(), + this.comparatorOutput() + ); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperSoundEventRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperSoundEventRegistryEntry.java new file mode 100644 index 000000000000..2a657db72688 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperSoundEventRegistryEntry.java @@ -0,0 +1,69 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.data.util.Conversions; +import java.util.Optional; +import net.kyori.adventure.key.Key; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import org.bukkit.Sound; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; +import static io.papermc.paper.registry.data.util.Checks.asConfigured; + +/** + * Not actually used for modifying {@link net.minecraft.core.registries.Registries#SOUND_EVENT} + * but for creating direct holders for other registries and direct {@link org.bukkit.craftbukkit.CraftSound}s. + */ +public class PaperSoundEventRegistryEntry implements SoundEventRegistryEntry { + + protected final Conversions conversions; + protected @Nullable ResourceLocation location; + protected @Nullable Float fixedRange; + + public PaperSoundEventRegistryEntry(final Conversions conversions, final @Nullable SoundEvent soundEvent) { + this.conversions = conversions; + if (soundEvent == null) { + return; + } + + this.location = soundEvent.location(); + this.fixedRange = soundEvent.fixedRange().orElse(null); + } + + @Override + public Key location() { + return PaperAdventure.asAdventure(asConfigured(this.location, "location")); + } + + @Override + public @Nullable Float fixedRange() { + return this.fixedRange; + } + + public static final class Builder extends PaperSoundEventRegistryEntry implements SoundEventRegistryEntry.Builder, PaperRegistryBuilder<SoundEvent, Sound> { + + public Builder(final Conversions conversions, final @Nullable SoundEvent soundEvent) { + super(conversions, soundEvent); + } + + @Override + public SoundEventRegistryEntry.Builder location(final Key location) { + this.location = PaperAdventure.asVanilla(asArgument(location, "location")); + return this; + } + + @Override + public SoundEventRegistryEntry.Builder fixedRange(final @Nullable Float fixedRange) { + this.fixedRange = fixedRange; + return this; + } + + @Override + public SoundEvent build() { + return new SoundEvent(asConfigured(this.location, "location"), Optional.ofNullable(this.fixedRange)); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/util/Checks.java b/paper-server/src/main/java/io/papermc/paper/registry/data/util/Checks.java index 9d61fad39895..24067e244502 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/util/Checks.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/util/Checks.java @@ -40,6 +40,13 @@ public static int asArgumentMin(final int value, final String field, final int m return value; } + public static float asArgumentMinExclusive(final float value, final String field, final float min) { + if (value <= min) { + throw new IllegalArgumentException("argument " + field + " must be (" + min + ",+inf)"); + } + return value; + } + private Checks() { } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java b/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java index b1710da835ce..6737573209a1 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java @@ -3,14 +3,22 @@ import com.google.common.base.Preconditions; import com.mojang.serialization.JavaOps; import io.papermc.paper.adventure.WrapperAwareSerializer; -import java.util.Optional; +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.PaperRegistryBuilderFactory; +import io.papermc.paper.registry.entry.RegistryEntryMeta; +import java.util.function.Consumer; import net.kyori.adventure.text.Component; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; +import org.bukkit.Keyed; import org.bukkit.craftbukkit.CraftRegistry; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.Nullable; public class Conversions { @@ -20,15 +28,7 @@ public static Conversions global() { if (globalInstance == null) { final RegistryAccess globalAccess = CraftRegistry.getMinecraftRegistry(); Preconditions.checkState(globalAccess != null, "Global registry access is not available"); - globalInstance = new Conversions(new RegistryOps.RegistryInfoLookup() { - @Override - public <T> Optional<RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryRef) { - final Registry<T> registry = globalAccess.lookupOrThrow(registryRef); - return Optional.of( - new RegistryOps.RegistryInfo<>(registry, registry, registry.registryLifecycle()) - ); - } - }); + globalInstance = new Conversions(new RegistryOps.HolderLookupAdapter(globalAccess)); } return globalInstance; } @@ -46,6 +46,10 @@ public RegistryOps.RegistryInfoLookup lookup() { return this.lookup; } + public <M> Holder.Reference<M> getReferenceHolder(final ResourceKey<M> key) { + return this.lookup.lookup(key.registryKey()).orElseThrow().getter().getOrThrow(key); + } + @Contract("null -> null; !null -> !null") public net.minecraft.network.chat.@Nullable Component asVanilla(final @Nullable Component adventure) { if (adventure == null) return null; @@ -55,4 +59,33 @@ public RegistryOps.RegistryInfoLookup lookup() { public Component asAdventure(final net.minecraft.network.chat.@Nullable Component vanilla) { return vanilla == null ? Component.empty() : this.serializer.deserialize(vanilla); } + + private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> RegistryEntryMeta.Buildable<M, A, B> getDirectHolderBuildableMeta(final ResourceKey<? extends Registry<M>> registryKey) { + final RegistryEntryMeta.Buildable<M, A, B> buildableMeta = PaperRegistries.getBuildableMeta(registryKey); + Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders"); + return buildableMeta; + } + + public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A createApiInstanceFromBuilder(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) { + final RegistryEntryMeta.Buildable<M, A, B> meta = getDirectHolderBuildableMeta(registryKey); + final PaperRegistryBuilderFactory<M, A, B> builderFactory = this.createRegistryBuilderFactory(registryKey, meta); + value.accept(builderFactory); + return meta.registryTypeMapper().convertDirectHolder(Holder.direct(builderFactory.requireBuilder().build())); + } + + public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> Holder<M> createHolderFromBuilder(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) { + final RegistryEntryMeta.Buildable<M, A, B> meta = getDirectHolderBuildableMeta(registryKey); + final PaperRegistryBuilderFactory<M, A, B> builderFactory = this.createRegistryBuilderFactory(registryKey, meta); + value.accept(builderFactory); + return Holder.direct(builderFactory.requireBuilder().build()); + } + + private <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> @NotNull PaperRegistryBuilderFactory<M, A, B> createRegistryBuilderFactory( + final ResourceKey<? extends Registry<M>> registryKey, + final RegistryEntryMeta.Buildable<M, A, B> buildableMeta + ) { + final HolderLookup.RegistryLookup<M> lookupForBuilders = this.lookup.lookupForValueCopyViaBuilders().lookupOrThrow(registryKey); + return new PaperRegistryBuilderFactory<>(registryKey, this, buildableMeta.builderFiller(), lookupForBuilders::getValueForCopying); + } + } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/entry/RegistryEntryBuilder.java b/paper-server/src/main/java/io/papermc/paper/registry/entry/RegistryEntryBuilder.java index bb7d19ee0079..f1b9e97b879e 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/entry/RegistryEntryBuilder.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/entry/RegistryEntryBuilder.java @@ -85,7 +85,7 @@ public <B extends PaperRegistryBuilder<M, A>> RegistryEntry<M, A> writable(final return this.create(filler, WRITABLE); } - private <B extends PaperRegistryBuilder<M, A>> RegistryEntry<M, A> create(final PaperRegistryBuilder.Filler<M, A, B> filler, final RegistryEntryMeta.RegistryModificationApiSupport support) { + public <B extends PaperRegistryBuilder<M, A>> RegistryEntry<M, A> create(final PaperRegistryBuilder.Filler<M, A, B> filler, final RegistryEntryMeta.RegistryModificationApiSupport support) { return new RegistryEntryImpl<>(new RegistryEntryMeta.Buildable<>(this.mcKey, this.apiKey, this.classToPreload, this.minecraftToBukkit, this.serializationUpdater, filler, support)); } } diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java index 671c37fa4096..f76184e80c4f 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java @@ -1,6 +1,16 @@ package io.papermc.testplugin; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.JukeboxPlayable; +import io.papermc.paper.event.player.ChatEvent; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import org.bukkit.JukeboxSong; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; public final class TestPlugin extends JavaPlugin implements Listener { @@ -12,4 +22,13 @@ public void onEnable() { // io.papermc.testplugin.brigtests.Registration.registerViaOnEnable(this); } + @EventHandler + public void onEvent(ChatEvent event) { + final ItemStack stick = new ItemStack(Material.STICK); + final Registry<JukeboxSong> registry = RegistryAccess.registryAccess().getRegistry(RegistryKey.JUKEBOX_SONG); + final JukeboxSong orThrow = registry.getOrThrow(TestPluginBootstrap.NEW); + stick.setData(DataComponentTypes.JUKEBOX_PLAYABLE, JukeboxPlayable.jukeboxPlayable(orThrow)); + event.getPlayer().getInventory().addItem(stick); + } + } diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java index fe2b287b257e..233696aa416a 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java @@ -2,13 +2,32 @@ import io.papermc.paper.plugin.bootstrap.BootstrapContext; import io.papermc.paper.plugin.bootstrap.PluginBootstrap; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.event.RegistryEvents; +import io.papermc.paper.registry.keys.JukeboxSongKeys; +import io.papermc.paper.registry.keys.SoundEventKeys; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import org.bukkit.JukeboxSong; import org.jetbrains.annotations.NotNull; public class TestPluginBootstrap implements PluginBootstrap { + static final TypedKey<JukeboxSong> NEW = JukeboxSongKeys.create(Key.key("test:test")); + @Override public void bootstrap(@NotNull BootstrapContext context) { // io.papermc.testplugin.brigtests.Registration.registerViaBootstrap(context); + + context.getLifecycleManager().registerEventHandler(RegistryEvents.JUKEBOX_SONG.freeze(), event -> { + // Do something with the event + event.registry().register(NEW, b -> { + b.comparatorOutput(2) + .description(Component.text("EPIC CUSTOM SOUND SONG")) + .lengthInSeconds(2) + .soundEvent(sb -> sb.copyFrom(SoundEventKeys.AMBIENT_BASALT_DELTAS_ADDITIONS).location(SoundEventKeys.BLOCK_STONE_BUTTON_CLICK_ON)); + }); + }); } }