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(Key key, RegistryKey registryKey) implements TypedKey { // 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, 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. + *

+ * The following values are required for each builder: + *

    + *
  • + * {@link #soundEvent(TypedKey)} or {@link #soundEvent(Consumer)} + *
  • + *
  • {@link #description(Component)}
  • + *
  • {@link #lengthInSeconds(float)}
  • + *
  • {@link #comparatorOutput(int)}
  • + *
+ */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends JukeboxSongRegistryEntry, RegistryBuilder { + + /** + * Sets the sound event for this song to a sound event present + * in the {@link io.papermc.paper.registry.RegistryKey#SOUND_EVENT} registry. + *

This will override {@link #soundEvent(Consumer)}

+ * + * @param soundEvent the sound event + * @return this builder + * @see #soundEvent(Consumer) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder soundEvent(TypedKey soundEvent); + + /** + * Sets the sound event for this song to a new sound event. + *

This will override {@link #soundEvent(TypedKey)}

+ * + * @param soundEvent the sound event + * @return this builder + * @see #soundEvent(TypedKey) + */ + @Contract(value = "_ -> this", mutates = "this") + Builder soundEvent(Consumer> 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. + *

+ * The following values are required for each builder: + *

    + *
  • {@link #location(Key)}
  • + *
+ */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends SoundEventRegistryEntry, RegistryBuilder { + + /** + * 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 PAINTING_VARIANT = create(RegistryKey.PAINTING_VARIANT); public static final RegistryEventProvider BANNER_PATTERN = create(RegistryKey.BANNER_PATTERN); public static final RegistryEventProvider DAMAGE_TYPE = create(RegistryKey.DAMAGE_TYPE); + public static final RegistryEventProvider 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 the type of the left value + * @param the type of the right value + */ +@NullMarked +public sealed interface Either 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 the type of the left value + * @param the type of the right value + */ + @Contract(value = "_ -> new", pure = true) + static Either.Left 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 the type of the left value + * @param the type of the right value + */ + @Contract(value = "_ -> new", pure = true) + static Either.Right right(final R value) { + return new EitherRight<>(value); + } + + /** + * Get an optional of the left value. + * + * @return an optional of the left value + */ + Optional left(); + + /** + * Get an optional of the right value. + * + * @return an optional of the right value + */ + Optional right(); + + /** + * A left value. + * + * @param the type of the left value + * @param the type of the right value + */ + sealed interface Left extends Either permits EitherLeft { + + /** + * Get the left value. + * + * @return the left value + */ + @Contract(pure = true) + L value(); + + @Override + default Optional left() { + return Optional.of(this.value()); + } + + @Override + default Optional right() { + return Optional.empty(); + } + } + + /** + * A right value. + * + * @param the type of the left value + * @param the type of the right value + */ + sealed interface Right extends Either permits EitherRight { + + /** + * Get the right value. + * + * @return the right value + */ + @Contract(pure = true) + R value(); + + @Override + default Optional left() { + return Optional.empty(); + } + + @Override + default Optional 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 value) implements Either.Left { +} 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(R value) implements Either.Right { +} 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() { + @Override ++ public Optional getValueForCopying(final ResourceKey resourceKey) { ++ return Optional.empty(); ++ } ++ ++ @Override + public ResourceKey> 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 implements WritableRegistry { +@@ -58,6 +58,19 @@ public class MappedRegistry implements WritableRegistry { return this.getTags(); } @@ -23531,7 +23531,7 @@ index 452c358c2cfa0c39e0b09853cd4a9a12c6ced65d..5f752603aa5611ce9d3dd44cc5b70c27 public MappedRegistry(ResourceKey> key, Lifecycle registryLifecycle) { this(key, registryLifecycle, false); } -@@ -116,6 +129,7 @@ public class MappedRegistry implements WritableRegistry { +@@ -123,6 +136,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 @@ -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 extends HolderLookup, HolderOwner { ++ ++ Optional getValueForCopying(ResourceKey resourceKey); // Paper - add method to get the value for pre-filling builders in the reg mod API ++ + ResourceKey> key(); + + Lifecycle registryLifecycle(); +@@ -80,6 +_,13 @@ + + default HolderLookup.RegistryLookup filterElements(final Predicate predicate) { + return new HolderLookup.RegistryLookup.Delegate() { ++ // Paper start - add getValueForCopying ++ @Override ++ public Optional getValueForCopying(final ResourceKey resourceKey) { ++ return this.parent().getValueForCopying(resourceKey).filter(predicate); ++ } ++ // Paper end - add getValueForCopying ++ + @Override + public HolderLookup.RegistryLookup parent() { + return RegistryLookup.this; +@@ -99,6 +_,13 @@ + + public interface Delegate extends HolderLookup.RegistryLookup { + HolderLookup.RegistryLookup parent(); ++ ++ // Paper start - add getValueForCopying ++ @Override ++ default Optional getValueForCopying(ResourceKey resourceKey) { ++ return this.parent().getValueForCopying(resourceKey); ++ } ++ // Paper end - add getValueForCopying + + @Override + default ResourceKey> 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 implements WritableRegistry { private final ResourceKey> key; private final ObjectList> byId = new ObjectArrayList<>(256); @@ -20,7 +20,14 @@ private boolean frozen; @Nullable private Map> unregisteredIntrusiveHolders; -+ public final Map temporaryUnfrozenMap = new HashMap<>(); // Paper - support pre-filling in registry mod API ++ // Paper start - support pre-filling in registry mod API ++ private final Map temporaryUnfrozenMap = new HashMap<>(); ++ ++ @Override ++ public Optional getValueForCopying(final ResourceKey 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> 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, Holder.Reference> elements + ) { + return new RegistrySetBuilder.EmptyTagRegistryLookup(owner) { ++ // Paper start - add getValueForCopying method ++ @Override ++ public Optional getValueForCopying(final ResourceKey resourceKey) { ++ return this.get(resourceKey).map(Holder.Reference::value); ++ } ++ // Paper end - add getValueForCopying method ++ + @Override + public ResourceKey> key() { + return registryKey; +@@ -121,6 +_,13 @@ + public Optional> lookup(ResourceKey> 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> SLOT_DISPLAY = registerSimple(Registries.SLOT_DISPLAY, SlotDisplays::bootstrap); public static final Registry RECIPE_BOOK_CATEGORY = registerSimple(Registries.RECIPE_BOOK_CATEGORY, RecipeBookCategories::bootstrap); public static final 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 java.util.Optional> lookup(final ResourceKey> registryRef) { -+ final Registry 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 Registry registerSimple(ResourceKey> key, BuiltInRegistries.RegistryBootstrap 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>, 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 Optional> lookup(ResourceKey> registryKey) { + return Optional.ofNullable((RegistryOps.RegistryInfo)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 ops, ResourceKey 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(HolderOwner owner, HolderGetter getter, Lifecycle elementsLifecycle) { +@@ -127,5 +_,7 @@ + + public interface RegistryInfoLookup { + Optional> lookup(ResourceKey> 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 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 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 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> implements RegistryBuilderFactory { // TODO remove Keyed + private final ResourceKey> registryKey; private final Conversions conversions; private final PaperRegistryBuilder.Filler builderFiller; - private final Function existingValueGetter; + private final Function, Optional> existingValueGetter; private @Nullable B builder; - public PaperRegistryBuilderFactory(final Conversions conversions, final PaperRegistryBuilder.Filler builderFiller, final Function existingValueGetter) { + public PaperRegistryBuilderFactory( + final ResourceKey> registryKey, + final Conversions conversions, + final PaperRegistryBuilder.Filler builderFiller, + final Function, Optional> 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 key) { this.validate(); - final M existing = this.existingValueGetter.apply(PaperAdventure.asVanilla(key)); - if (existing == null) { + final Optional 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 registerWithListeners(final Registry registry, final ResourceLoc * For {@link Registry#register(Registry, ResourceKey, Object)} */ public M registerWithListeners(final Registry registry, final ResourceKey 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 Holder.Reference registerForHolderWithListeners(final Registry * For {@link Registry#registerForHolder(Registry, ResourceKey, Object)} */ public Holder.Reference registerForHolderWithListeners(final Registry registry, final ResourceKey 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 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 key, final Consumer> value, final Conversions conversions) { final ResourceKey resourceKey = PaperRegistries.toNms(key); this.registry.validateWrite(resourceKey); - final PaperRegistryBuilderFactory builderFactory = new PaperRegistryBuilderFactory<>(conversions, this.meta.builderFiller(), this.registry.temporaryUnfrozenMap::get); + final PaperRegistryBuilderFactory 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 > A create(final ResourceKey> registryKey, final Consumer> value) { - final RegistryEntryMeta.Buildable buildableMeta = PaperRegistries.getBuildableMeta(registryKey); - Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders"); - final PaperRegistryBuilderFactory 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> 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; + 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, SoundEventRegistryEntry> soundEvent() { + final Holder 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 { + + public Builder(final Conversions conversions, final @Nullable JukeboxSong internal) { + super(conversions, internal); + } + + @Override + public JukeboxSongRegistryEntry.Builder soundEvent(final TypedKey soundEvent) { + this.soundEvent = this.conversions.getReferenceHolder(PaperRegistries.toNms(asArgument(soundEvent, "soundEvent"))); + return this; + } + + @Override + public JukeboxSongRegistryEntry.Builder soundEvent(final Consumer> 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 { + + 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 Optional> lookup(final ResourceKey> registryRef) { - final Registry 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 Holder.Reference getReferenceHolder(final ResourceKey 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 > RegistryEntryMeta.Buildable getDirectHolderBuildableMeta(final ResourceKey> registryKey) { + final RegistryEntryMeta.Buildable buildableMeta = PaperRegistries.getBuildableMeta(registryKey); + Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders"); + return buildableMeta; + } + + public > A createApiInstanceFromBuilder(final ResourceKey> registryKey, final Consumer> value) { + final RegistryEntryMeta.Buildable meta = getDirectHolderBuildableMeta(registryKey); + final PaperRegistryBuilderFactory builderFactory = this.createRegistryBuilderFactory(registryKey, meta); + value.accept(builderFactory); + return meta.registryTypeMapper().convertDirectHolder(Holder.direct(builderFactory.requireBuilder().build())); + } + + public > Holder createHolderFromBuilder(final ResourceKey> registryKey, final Consumer> value) { + final RegistryEntryMeta.Buildable meta = getDirectHolderBuildableMeta(registryKey); + final PaperRegistryBuilderFactory builderFactory = this.createRegistryBuilderFactory(registryKey, meta); + value.accept(builderFactory); + return Holder.direct(builderFactory.requireBuilder().build()); + } + + private > @NotNull PaperRegistryBuilderFactory createRegistryBuilderFactory( + final ResourceKey> registryKey, + final RegistryEntryMeta.Buildable buildableMeta + ) { + final HolderLookup.RegistryLookup 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 > RegistryEntry writable(final return this.create(filler, WRITABLE); } - private > RegistryEntry create(final PaperRegistryBuilder.Filler filler, final RegistryEntryMeta.RegistryModificationApiSupport support) { + public > RegistryEntry create(final PaperRegistryBuilder.Filler 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 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 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)); + }); + }); } }