From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Wed, 2 Mar 2022 13:36:21 -0800 Subject: [PATCH] Add RegistryAccess for managing registries diff --git a/src/main/java/io/papermc/paper/registry/Reference.java b/src/main/java/io/papermc/paper/registry/Reference.java new file mode 100644 index 0000000000000000000000000000000000000000..d8656772e0c983df7c40ddc367a73ce473348339 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/Reference.java @@ -0,0 +1,47 @@ +package io.papermc.paper.registry; + +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a reference to a server-backed registry value that may + * change. + * + * @param type of the value + */ +@Deprecated(forRemoval = true, since = "1.20.6") +public interface Reference extends Keyed { + + /** + * Gets the value from the registry with the key. + * + * @return the value + * @throws java.util.NoSuchElementException if there is no value with this key + */ + @Deprecated(forRemoval = true, since = "1.20.6") + @NotNull T value(); + + /** + * Gets the value from the registry with the key. + * + * @return the value or null if it doesn't exist + */ + @Deprecated(forRemoval = true, since = "1.20.6") + @Nullable T valueOrNull(); + + /** + * Creates a reference to a registered value. + * + * @param registry the registry the value is located in + * @param key the key to the value + * @param the type of the value + * @return a reference + */ + @Deprecated(forRemoval = true, since = "1.20.6") + static @NotNull Reference create(@NotNull Registry registry, @NotNull NamespacedKey key) { + return new ReferenceImpl<>(registry, key); + } +} diff --git a/src/main/java/io/papermc/paper/registry/ReferenceImpl.java b/src/main/java/io/papermc/paper/registry/ReferenceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f29e76a6b66ddfec12ddf8db6dcb2df6083b5982 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/ReferenceImpl.java @@ -0,0 +1,31 @@ +package io.papermc.paper.registry; + +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.NoSuchElementException; + +record ReferenceImpl(@NotNull Registry registry, @NotNull NamespacedKey key) implements Reference { + + @Override + public @NotNull T value() { + final T value = this.registry.get(this.key); + if (value == null) { + throw new NoSuchElementException("No such value with key " + this.key); + } + return value; + } + + @Override + public @Nullable T valueOrNull() { + return this.registry.get(this.key); + } + + @Override + public @NotNull NamespacedKey getKey() { + return this.key; + } +} diff --git a/src/main/java/io/papermc/paper/registry/RegistryAccess.java b/src/main/java/io/papermc/paper/registry/RegistryAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..00a3a4a196808b4b5c84ecccbfb1ea0e3825146a --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/RegistryAccess.java @@ -0,0 +1,50 @@ +package io.papermc.paper.registry; + +import org.bukkit.Keyed; +import org.bukkit.Registry; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Used for accessing different {@link Registry} instances + * by a {@link RegistryKey}. Get the main instance of {@link RegistryAccess} + * with {@link RegistryAccess#registryAccess()}. + */ +@NullMarked +@ApiStatus.NonExtendable +public interface RegistryAccess { + + /** + * Get the {@link RegistryAccess} instance for the server. + * + * @return the RegistryAccess instance + */ + static RegistryAccess registryAccess() { + return RegistryAccessHolder.INSTANCE.orElseThrow(() -> new IllegalStateException("No RegistryAccess implementation found")); + } + + /** + * Gets the registry based on the type. + * + * @param type the type + * @return the registry or null if none found + * @param the type + * @deprecated use {@link #getRegistry(RegistryKey)} with keys from {@link RegistryKey} + */ + @Deprecated(since = "1.20.6", forRemoval = true) + @Nullable Registry getRegistry(Class type); + + /** + * Gets the registry with the specified key. + * + * @param registryKey the key + * @return the registry + * @param the type + * @throws java.util.NoSuchElementException if no registry with the key is found + * @throws IllegalArgumentException if the registry is not available yet + */ + // Future note: We should have no trouble removing this generic qualifier when + // registry types no longer have to be "keyed" as it shouldn't break ABI or API. + Registry getRegistry(RegistryKey registryKey); +} diff --git a/src/main/java/io/papermc/paper/registry/RegistryAccessHolder.java b/src/main/java/io/papermc/paper/registry/RegistryAccessHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..b89e19c070f97c9662f1e16309446494b30aa7c9 --- /dev/null +++ b/src/main/java/io/papermc/paper/registry/RegistryAccessHolder.java @@ -0,0 +1,12 @@ +package io.papermc.paper.registry; + +import java.util.Optional; +import java.util.ServiceLoader; + +final class RegistryAccessHolder { + + static final Optional INSTANCE = ServiceLoader.load(RegistryAccess.class).findFirst(); + + private RegistryAccessHolder() { + } +} diff --git a/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java b/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java index 80e3e64f47ac55a4978c9e5b430e2f2d1c871d1b..ac68dfce754dc7e014bb31bba32d9b246ffd411c 100644 --- a/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java +++ b/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java @@ -12,6 +12,17 @@ record RegistryKeyImpl(Key key) implements RegistryKey { static final Set> REGISTRY_KEYS = Sets.newIdentityHashSet(); + // override equals and hashCode to this can be used to simulate an "identity" hashmap + @Override + public boolean equals(final @Nullable Object obj) { + return obj == this; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + static RegistryKey create(@Subst("some_key") final String key) { final RegistryKey registryKey = createInternal(key); REGISTRY_KEYS.add(registryKey); diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index e0f652117e585882693736de8165ae9c689e1d68..fbe14c327ee9c1ac07893853ca7c699e81225281 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -2410,8 +2410,11 @@ public final class Bukkit { * @param tClass of the registry to get * @param type of the registry * @return the corresponding registry or null if not present + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} + * with keys from {@link io.papermc.paper.registry.RegistryKey} */ @Nullable + @Deprecated(since = "1.20.6") public static Registry getRegistry(@NotNull Class tClass) { return server.getRegistry(tClass); } diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java index fbede496b05c4b9b1ecd12e711a100586776d469..3777f9c92d0c183d0ab5e28bbe73f2125babf61c 100644 --- a/src/main/java/org/bukkit/Registry.java +++ b/src/main/java/org/bukkit/Registry.java @@ -88,8 +88,10 @@ public interface Registry extends Iterable { * Server banner patterns. * * @see PatternType + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#BANNER_PATTERN} */ - Registry BANNER_PATTERN = Objects.requireNonNull(Bukkit.getRegistry(PatternType.class), "No registry present for Pattern Type. This is a bug."); + @Deprecated(since = "1.21") // Paper + Registry BANNER_PATTERN = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(PatternType.class), "No registry present for PatternType. This is a bug."); // Paper /** * Server biomes. * @@ -103,7 +105,7 @@ public interface Registry extends Iterable { * @apiNote BlockType is not ready for public usage yet */ @ApiStatus.Internal - Registry BLOCK = Objects.requireNonNull(Bukkit.getRegistry(BlockType.class), "No registry present for BlockType. This is a bug."); + Registry BLOCK = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.BLOCK); // Paper /** * Custom boss bars. * @@ -135,13 +137,15 @@ public interface Registry extends Iterable { * * @see Cat.Type */ - Registry CAT_VARIANT = Objects.requireNonNull(Bukkit.getRegistry(Cat.Type.class), "No registry present for Cat Type. This is a bug."); + Registry CAT_VARIANT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.CAT_VARIANT); // Paper /** * Server enchantments. * * @see Enchantment + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#ENCHANTMENT} */ - Registry ENCHANTMENT = Objects.requireNonNull(Bukkit.getRegistry(Enchantment.class), "No registry present for Enchantment. This is a bug."); + @Deprecated(since = "1.21") + Registry ENCHANTMENT = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Enchantment.class), "No registry present for Enchantment. This is a bug."); // Paper /** * Server entity types. * @@ -153,7 +157,7 @@ public interface Registry extends Iterable { * * @see MusicInstrument */ - Registry INSTRUMENT = Objects.requireNonNull(Bukkit.getRegistry(MusicInstrument.class), "No registry present for MusicInstrument. This is a bug."); + Registry INSTRUMENT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.INSTRUMENT); // Paper /** * Server item types. * @@ -161,7 +165,7 @@ public interface Registry extends Iterable { * @apiNote ItemType is not ready for public usage yet */ @ApiStatus.Internal - Registry ITEM = Objects.requireNonNull(Bukkit.getRegistry(ItemType.class), "No registry present for ItemType. This is a bug."); + Registry ITEM = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.ITEM); // Paper /** * Default server loot tables. * @@ -180,13 +184,13 @@ public interface Registry extends Iterable { * @see MenuType */ @ApiStatus.Experimental - Registry MENU = Objects.requireNonNull(Bukkit.getRegistry(MenuType.class), "No registry present for MenuType. This is a bug."); + Registry MENU = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.MENU); // Paper /** * Server mob effects. * * @see PotionEffectType */ - Registry EFFECT = Objects.requireNonNull(Bukkit.getRegistry(PotionEffectType.class), "No registry present for PotionEffectType. This is a bug."); + Registry EFFECT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.MOB_EFFECT); // Paper /** * Server particles. * @@ -209,14 +213,16 @@ public interface Registry extends Iterable { * Server structures. * * @see Structure + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#STRUCTURE} */ - Registry STRUCTURE = Bukkit.getRegistry(Structure.class); + @Deprecated(since = "1.20.6") // Paper + Registry STRUCTURE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Structure.class), "No registry present for Structure. This is a bug."); // Paper /** * Server structure types. * * @see StructureType */ - Registry STRUCTURE_TYPE = Bukkit.getRegistry(StructureType.class); + Registry STRUCTURE_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.STRUCTURE_TYPE); // Paper /** * Sound keys. * @@ -227,40 +233,47 @@ public interface Registry extends Iterable { * Trim materials. * * @see TrimMaterial + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#TRIM_MATERIAL} */ - Registry TRIM_MATERIAL = Bukkit.getRegistry(TrimMaterial.class); + @Deprecated(since = "1.20.6") // Paper + Registry TRIM_MATERIAL = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(TrimMaterial.class), "No registry present for TrimMaterial. This is a bug."); // Paper /** * Trim patterns. * * @see TrimPattern + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#TRIM_PATTERN} */ - Registry TRIM_PATTERN = Bukkit.getRegistry(TrimPattern.class); + @Deprecated(since = "1.20.6") + Registry TRIM_PATTERN = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(TrimPattern.class), "No registry present for TrimPattern. This is a bug."); // Paper /** * Damage types. * * @see DamageType + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#DAMAGE_TYPE} */ - @ApiStatus.Experimental - Registry DAMAGE_TYPE = Objects.requireNonNull(Bukkit.getRegistry(DamageType.class), "No registry present for DamageType. This is a bug."); + @Deprecated(since = "1.20.6") + Registry DAMAGE_TYPE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(DamageType.class), "No registry present for DamageType. This is a bug."); // Paper /** * Jukebox songs. * * @see JukeboxSong + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#JUKEBOX_SONG} */ @ApiStatus.Experimental - Registry JUKEBOX_SONG = Objects.requireNonNull(Bukkit.getRegistry(JukeboxSong.class), "No registry present for JukeboxSong. This is a bug."); + @Deprecated(since = "1.21") + Registry JUKEBOX_SONG = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(JukeboxSong.class), "No registry present for JukeboxSong. This is a bug."); // Paper /** * Villager profession. * * @see Villager.Profession */ - Registry VILLAGER_PROFESSION = Objects.requireNonNull(Bukkit.getRegistry(Villager.Profession.class), "No registry present for Villager Profession. This is a bug."); + Registry VILLAGER_PROFESSION = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.VILLAGER_PROFESSION); // Paper /** * Villager type. * * @see Villager.Type */ - Registry VILLAGER_TYPE = Objects.requireNonNull(Bukkit.getRegistry(Villager.Type.class), "No registry present for Villager Type. This is a bug."); + Registry VILLAGER_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.VILLAGER_TYPE); // Paper /** * Memory Keys. * @@ -297,25 +310,27 @@ public interface Registry extends Iterable { * * @see Frog.Variant */ - Registry FROG_VARIANT = Objects.requireNonNull(Bukkit.getRegistry(Frog.Variant.class), "No registry present for Frog Variant. This is a bug."); + Registry FROG_VARIANT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.FROG_VARIANT); // Paper /** * Wolf variants. * * @see Wolf.Variant + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#WOLF_VARIANT} */ - Registry WOLF_VARIANT = Objects.requireNonNull(Bukkit.getRegistry(Wolf.Variant.class), "No registry present for Wolf Variant. This is a bug."); + @Deprecated(since = "1.20.6") + Registry WOLF_VARIANT = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Wolf.Variant.class), "No registry present for Wolf$Variant. This is a bug."); // Paper /** * Map cursor types. * * @see MapCursor.Type */ - Registry MAP_DECORATION_TYPE = Objects.requireNonNull(Bukkit.getRegistry(MapCursor.Type.class), "No registry present for MapCursor Type. This is a bug."); + Registry MAP_DECORATION_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.MAP_DECORATION_TYPE); // Paper /** * Game events. * * @see GameEvent */ - Registry GAME_EVENT = Objects.requireNonNull(Bukkit.getRegistry(GameEvent.class), "No registry present for GameEvent. This is a bug."); + Registry GAME_EVENT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.GAME_EVENT); // Paper /** * Get the object by its key. * diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index 90111a9ae0b7bdd6e46e869398dc6b9898b5f87e..943f8881ea23481ea5d5125b6ec7c9c6f763f0b0 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -2059,8 +2059,11 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * @param tClass of the registry to get * @param type of the registry * @return the corresponding registry or null if not present + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} + * with keys from {@link io.papermc.paper.registry.RegistryKey} */ @Nullable + @Deprecated(since = "1.20.6") // Paper Registry getRegistry(@NotNull Class tClass); /** diff --git a/src/test/java/io/papermc/paper/registry/TestRegistryAccess.java b/src/test/java/io/papermc/paper/registry/TestRegistryAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..f5ece852f97017f71bc129e194cb212979b2537b --- /dev/null +++ b/src/test/java/io/papermc/paper/registry/TestRegistryAccess.java @@ -0,0 +1,20 @@ +package io.papermc.paper.registry; + +import org.bukkit.Keyed; +import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class TestRegistryAccess implements RegistryAccess { + + @Override + @Deprecated(since = "1.20.6", forRemoval = true) + public @Nullable Registry getRegistry(final @NotNull Class type) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public @NotNull Registry getRegistry(final @NotNull RegistryKey registryKey) { + throw new UnsupportedOperationException("Not supported"); + } +} diff --git a/src/test/java/org/bukkit/support/TestServer.java b/src/test/java/org/bukkit/support/TestServer.java index c79faf4197f9c0a7256cefe2b001182102d2b796..55f1bc7d01e5184172559c43b8327ac86d58041b 100644 --- a/src/test/java/org/bukkit/support/TestServer.java +++ b/src/test/java/org/bukkit/support/TestServer.java @@ -37,36 +37,11 @@ public final class TestServer { when(instance.getBukkitVersion()).thenReturn("BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion()); - Map, Registry> registers = new HashMap<>(); - when(instance.getRegistry(any())).then(invocationOnMock -> registers.computeIfAbsent(invocationOnMock.getArgument(0), aClass -> new Registry<>() { - private final Map cache = new HashMap<>(); - - @Override - public Keyed get(NamespacedKey key) { - Class theClass; - // Some registries have extra Typed classes such as BlockType and ItemType. - // To avoid class cast exceptions during init mock the Typed class. - // To get the correct class, we just use the field type. - try { - theClass = (Class) aClass.getField(key.getKey().toUpperCase(Locale.ROOT).replace('.', '_')).getType(); - } catch (ClassCastException | NoSuchFieldException e) { - throw new RuntimeException(e); - } - - return cache.computeIfAbsent(key, key2 -> mock(theClass, withSettings().stubOnly())); - } - - @NotNull - @Override - public Stream stream() { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException("Not supported"); - } - })); + // Paper start - RegistryAccess + when(instance.getRegistry(any())).then(invocationOnMock -> { + return io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(((Class)invocationOnMock.getArgument(0))); + }); + // Paper end - RegistryAccess UnsafeValues unsafeValues = mock(withSettings().stubOnly()); when(instance.getUnsafe()).thenReturn(unsafeValues); diff --git a/src/test/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess b/src/test/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess new file mode 100644 index 0000000000000000000000000000000000000000..f0a5e6d6b99aeef349fe465080ef2ff7b58617a6 --- /dev/null +++ b/src/test/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess @@ -0,0 +1 @@ +io.papermc.paper.registry.TestRegistryAccess