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 bc224c349b..0d1d444e1b 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 @@ -41,6 +41,14 @@ freeze(); validate(REGISTRY); } +@@ -338,6 +_,7 @@ + if (supplier.get() == null) { + LOGGER.error("Unable to bootstrap registry '{}'", resourceLocation); + } ++ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(resourceLocation)); // Paper - lock reference holder creation + }); + } + @@ -346,6 +_,7 @@ for (Registry registry : REGISTRY) { diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java index 3be7e0a1fb..6400df6ea1 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java @@ -1,5 +1,6 @@ package io.papermc.paper.registry; +import com.google.common.base.Preconditions; import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; import io.papermc.paper.registry.legacy.DelayedRegistry; @@ -13,6 +14,7 @@ import java.util.stream.Collectors; import net.minecraft.resources.ResourceKey; import org.bukkit.Keyed; import org.bukkit.Registry; +import org.bukkit.craftbukkit.CraftRegistry; import org.jetbrains.annotations.VisibleForTesting; import org.jspecify.annotations.Nullable; @@ -104,6 +106,21 @@ public class PaperRegistryAccess implements RegistryAccess { this.registerRegistry(resourceKey, registry, false); } + public void lockReferenceHolders(final ResourceKey> resourceKey) { + final RegistryEntry entry = PaperRegistries.getEntry(resourceKey); + if (entry == null || !(entry.meta() instanceof final RegistryEntryMeta.ServerSide serverSide) || !serverSide.registryTypeMapper().supportsDirectHolders()) { + return; + } + final CraftRegistry registry = (CraftRegistry) this.getRegistry(entry.apiKey()); + Preconditions.checkState(registry.isUnloaded(), "Registry %s is already loaded", resourceKey); + try { + Class.forName(serverSide.classToPreload().getName()); // this should always trigger the initialization of the class + } catch (final ClassNotFoundException e) { + throw new IllegalStateException("Failed to load class " + serverSide.classToPreload().getName(), e); + } + registry.lockReferenceHolders(); + } + @SuppressWarnings("unchecked") // this method should be called right after any new MappedRegistry instances are created to later be used by the server. private > void registerRegistry(final ResourceKey> resourceKey, final net.minecraft.core.Registry registry, final boolean replace) { final @Nullable RegistryEntry entry = PaperRegistries.getEntry(resourceKey); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java index 332215f8b4..ab0a1eed5c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java @@ -3,66 +3,26 @@ package org.bukkit.craftbukkit; import com.google.common.base.Preconditions; import io.papermc.paper.registry.entry.RegistryEntryMeta; import io.papermc.paper.util.Holderable; +import io.papermc.paper.util.MCUtil; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Optional; import java.util.function.BiFunction; import java.util.stream.Stream; import net.minecraft.core.Holder; +import net.minecraft.core.HolderOwner; import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; -import org.bukkit.Art; -import org.bukkit.Fluid; -import org.bukkit.GameEvent; -import org.bukkit.JukeboxSong; import org.bukkit.Keyed; -import org.bukkit.MusicInstrument; import org.bukkit.NamespacedKey; import org.bukkit.Particle; import org.bukkit.Registry; -import org.bukkit.Sound; -import org.bukkit.attribute.Attribute; -import org.bukkit.block.Biome; -import org.bukkit.block.BlockType; -import org.bukkit.block.banner.PatternType; -import org.bukkit.craftbukkit.attribute.CraftAttribute; -import org.bukkit.craftbukkit.block.CraftBiome; -import org.bukkit.craftbukkit.block.CraftBlockType; -import org.bukkit.craftbukkit.block.banner.CraftPatternType; -import org.bukkit.craftbukkit.damage.CraftDamageType; -import org.bukkit.craftbukkit.enchantments.CraftEnchantment; -import org.bukkit.craftbukkit.entity.CraftCat; -import org.bukkit.craftbukkit.entity.CraftFrog; -import org.bukkit.craftbukkit.entity.CraftVillager; -import org.bukkit.craftbukkit.entity.CraftWolf; -import org.bukkit.craftbukkit.generator.structure.CraftStructure; -import org.bukkit.craftbukkit.generator.structure.CraftStructureType; -import org.bukkit.craftbukkit.inventory.CraftItemType; -import org.bukkit.craftbukkit.inventory.CraftMenuType; -import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; -import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern; import org.bukkit.craftbukkit.legacy.FieldRename; -import org.bukkit.craftbukkit.map.CraftMapCursor; -import org.bukkit.craftbukkit.potion.CraftPotionEffectType; import org.bukkit.craftbukkit.util.ApiVersion; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.craftbukkit.util.Handleable; -import org.bukkit.damage.DamageType; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Cat; import org.bukkit.entity.EntityType; -import org.bukkit.entity.Frog; -import org.bukkit.entity.Villager; -import org.bukkit.entity.Wolf; -import org.bukkit.generator.structure.Structure; -import org.bukkit.generator.structure.StructureType; -import org.bukkit.inventory.ItemType; -import org.bukkit.inventory.MenuType; -import org.bukkit.inventory.meta.trim.TrimMaterial; -import org.bukkit.inventory.meta.trim.TrimPattern; -import org.bukkit.map.MapCursor; -import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; public class CraftRegistry implements Registry { @@ -202,7 +162,8 @@ public class CraftRegistry implements Registry { private final net.minecraft.core.Registry minecraftRegistry; private final io.papermc.paper.registry.entry.RegistryTypeMapper minecraftToBukkit; // Paper - switch to Holder private final BiFunction serializationUpdater; // Paper - rename to make it *clear* what it is *only* for - private boolean init; + private final InvalidHolderOwner invalidHolderOwner = new InvalidHolderOwner(); + private boolean lockReferenceHolders; public CraftRegistry(Class bukkitClass, net.minecraft.core.Registry minecraftRegistry, BiFunction minecraftToBukkit, BiFunction serializationUpdater) { // Paper - relax preload class // Paper start - switch to Holder @@ -217,6 +178,16 @@ public class CraftRegistry implements Registry { this.minecraftRegistry = minecraftRegistry; this.minecraftToBukkit = minecraftToBukkit; this.serializationUpdater = serializationUpdater; + this.lockReferenceHolders = !this.minecraftToBukkit.supportsDirectHolders(); + } + + public boolean isUnloaded() { + return this.cache.isEmpty(); + } + + public void lockReferenceHolders() { + Preconditions.checkState(!this.lockReferenceHolders, "Reference holders are already locked"); + this.lockReferenceHolders = true; } // Paper - inline into CraftRegistry#get(Registry, NamespacedKey, ApiVersion) above @@ -228,28 +199,19 @@ public class CraftRegistry implements Registry { return cached; } - // Make sure that the bukkit class is loaded before creating an instance. - // This ensures that only one instance with a given key is created. - // - // Without this code (when bukkit class is not loaded): - // Registry#get -> #createBukkit -> (load class -> create default) -> put in cache - // Result: Registry#get != . for possible one registry item - // - // With this code (when bukkit class is not loaded): - // Registry#get -> (load class -> create default) -> Registry#get -> get from cache - // Result: Registry#get == . - if (!this.init) { - this.init = true; - try { - Class.forName(this.bukkitClass.getName()); - } catch (ClassNotFoundException e) { - throw new RuntimeException("Could not load registry class " + this.bukkitClass, e); - } - - return this.get(namespacedKey); + final Optional> holderOptional = this.minecraftRegistry.get(CraftNamespacedKey.toMinecraft(namespacedKey)); + final Holder.Reference holder; + if (holderOptional.isPresent()) { + holder = holderOptional.get(); + } else if (!this.lockReferenceHolders && this.minecraftToBukkit.supportsDirectHolders()) { // only works if its Holderable + // we lock the reference holders after the preload class has been initialized + // this is to support the vanilla mechanic of preventing vanilla registry entries being loaded. We need + // to create something to fill the API constant fields, so we create a dummy reference holder. + holder = Holder.Reference.createStandAlone(this.invalidHolderOwner, MCUtil.toResourceKey(this.minecraftRegistry.key(), namespacedKey)); + } else { + holder = null; } - - B bukkit = this.createBukkit(namespacedKey, this.minecraftRegistry.get(CraftNamespacedKey.toMinecraft(namespacedKey)).orElse(null)); // Paper - switch to Holder + final B bukkit = this.createBukkit(namespacedKey, holder); if (bukkit == null) { return null; } @@ -320,4 +282,7 @@ public class CraftRegistry implements Registry { return new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(key, namedHolderSet); } // Paper end - RegistrySet API + + final class InvalidHolderOwner implements HolderOwner { + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/paper-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java index 34934f0dbe..36825fb8f0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java @@ -1,11 +1,11 @@ package org.bukkit.craftbukkit.enchantments; import com.google.common.base.Preconditions; +import io.papermc.paper.util.Holderable; import java.util.Locale; import net.minecraft.Util; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.tags.EnchantmentTags; import org.bukkit.NamespacedKey; import org.bukkit.Registry; @@ -13,13 +13,12 @@ import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.legacy.FieldRename; import org.bukkit.craftbukkit.util.ApiVersion; -import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.enchantments.EnchantmentWrapper; import org.bukkit.inventory.ItemStack; -public class CraftEnchantment extends Enchantment implements Handleable { +public class CraftEnchantment extends Enchantment implements Holderable { public static Enchantment minecraftToBukkit(net.minecraft.world.item.enchantment.Enchantment minecraft) { return CraftRegistry.minecraftToBukkit(minecraft, Registries.ENCHANTMENT, Registry.ENCHANTMENT); @@ -56,22 +55,20 @@ public class CraftEnchantment extends Enchantment implements Handleable handle; - public CraftEnchantment(NamespacedKey key, net.minecraft.world.item.enchantment.Enchantment handle) { - this.key = key; - this.handle = CraftRegistry.getMinecraftRegistry(Registries.ENCHANTMENT).wrapAsHolder(handle); + public CraftEnchantment(Holder holder) { + this.handle = holder; } @Override - public net.minecraft.world.item.enchantment.Enchantment getHandle() { - return this.handle.value(); + public Holder getHolder() { + return this.handle; } @Override public NamespacedKey getKey() { - return this.key; + return Holderable.super.getKey(); } @Override @@ -251,24 +248,16 @@ public class CraftEnchantment extends Enchantment implements Handleable