diff --git a/.editorconfig b/.editorconfig index 9c12172107..65898b5181 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,6 +31,11 @@ ij_java_generate_final_locals = true ij_java_generate_final_parameters = true ij_java_method_parameters_new_line_after_left_paren = true ij_java_method_parameters_right_paren_on_new_line = true +ij_java_use_fq_class_names = false +ij_java_class_names_in_javadoc = 1 + +[paper-server/src/minecraft/java/**/*.java] +ij_java_use_fq_class_names = true [paper-server/src/minecraft/resources/data/**/*.json] indent_size = 2 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..36c52e0a7a 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 @@ -26,7 +26,7 @@ ResourceLocation resourceLocation = key.location(); LOADERS.put(resourceLocation, () -> bootstrap.run(registry)); WRITABLE_REGISTRY.register((ResourceKey)key, registry, RegistrationInfo.BUILT_IN); -@@ -328,7 +_,14 @@ +@@ -328,16 +_,34 @@ } public static void bootStrap() { @@ -41,6 +41,26 @@ freeze(); validate(REGISTRY); } + + private static void createContents() { ++ // Paper start - class-load org.bukkit.Registry ++ // we have to class-load Registry here to create all the CraftRegistry instances ++ // that will be created when Registry is class-loaded before RegistryAccess#getRegistry ++ // would try to create them in lockReferenceHolder ++ try { ++ Class.forName(org.bukkit.Registry.class.getName()); ++ } catch (final ClassNotFoundException ex) { ++ throw new RuntimeException(ex); ++ } ++ // Paper end - class-load org.bukkit.Registry + LOADERS.forEach((resourceLocation, supplier) -> { + 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/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch index c625d649d2..97a14239e6 100644 --- a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch @@ -34,11 +34,12 @@ } catch (Exception var14) { loadingErrors.put( resourceKey, -@@ -283,7 +_,8 @@ +@@ -283,7 +_,9 @@ } } - TagLoader.loadTagsForRegistry(resourceManager, registry); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(registry.key()); // Paper - lock reference holders + io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), conversions); // Paper - run pre-freeze listeners + TagLoader.loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - tag lifecycle - add cause } 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..ab8fba3442 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 @@ -13,6 +13,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; @@ -72,7 +73,7 @@ public class PaperRegistryAccess implements RegistryAccess { if (PaperRegistries.getEntry(key) == null) { throw new NoSuchElementException(key + " is not a valid registry key"); } - final @Nullable RegistryHolder<T> registryHolder = (RegistryHolder<T>) this.registries.get(key); + final RegistryHolder<T> registryHolder = (RegistryHolder<T>) this.registries.get(key); if (registryHolder == null) { throw new IllegalArgumentException(key + " points to a registry that is not available yet"); } @@ -104,13 +105,22 @@ public class PaperRegistryAccess implements RegistryAccess { this.registerRegistry(resourceKey, registry, false); } + public <M> void lockReferenceHolders(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey) { + final RegistryEntry<M, Keyed> entry = PaperRegistries.getEntry(resourceKey); + if (entry == null || !(entry.meta() instanceof final RegistryEntryMeta.ServerSide<M, Keyed> serverSide) || !serverSide.registryTypeMapper().supportsDirectHolders()) { + return; + } + final CraftRegistry<?, M> registry = (CraftRegistry<?, M>) this.getRegistry(entry.apiKey()); + 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 <M, B extends Keyed, R extends Registry<B>> void registerRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry, final boolean replace) { - final @Nullable RegistryEntry<M, B> entry = PaperRegistries.getEntry(resourceKey); + final RegistryEntry<M, B> entry = PaperRegistries.getEntry(resourceKey); if (entry == null) { // skip registries that don't have API entries return; } - final @Nullable RegistryHolder<B> registryHolder = (RegistryHolder<B>) this.registries.get(entry.apiKey()); + final RegistryHolder<B> registryHolder = (RegistryHolder<B>) this.registries.get(entry.apiKey()); if (registryHolder == null || replace) { // if the holder doesn't exist yet, or is marked as "replaceable", put it in the map. this.registries.put(entry.apiKey(), entry.createRegistryHolder(registry)); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java index 520c3e2e1d..7ad11769a1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java @@ -41,9 +41,6 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc. return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(string, Instrument.CODEC, Registry.INSTRUMENT); // Paper - switch to Holder } - private final NamespacedKey key; - private final Instrument handle; - // Paper start - switch to Holder @Override public boolean equals(final Object o) { @@ -63,8 +60,6 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc. private final Holder<Instrument> holder; public CraftMusicInstrument(Holder<Instrument> holder) { this.holder = holder; - this.key = holder.unwrapKey().map(io.papermc.paper.util.MCUtil::fromResourceKey).orElse(null); - this.handle = holder.value(); // Paper end - switch to Holder } 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 2762dd2590..286bc2271f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java @@ -6,12 +6,15 @@ import io.papermc.paper.registry.set.NamedRegistryKeySetImpl; import io.papermc.paper.registry.tag.Tag; import io.papermc.paper.util.Holderable; import java.util.Collection; +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.resources.ResourceKey; import org.bukkit.Keyed; @@ -162,7 +165,8 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> { private final net.minecraft.core.Registry<M> minecraftRegistry; private final io.papermc.paper.registry.entry.RegistryTypeMapper<M, B> minecraftToBukkit; // Paper - switch to Holder private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> 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<M> minecraftRegistry, BiFunction<? super NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class // Paper start - switch to Holder @@ -177,6 +181,22 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> { this.minecraftRegistry = minecraftRegistry; this.minecraftToBukkit = minecraftToBukkit; this.serializationUpdater = serializationUpdater; + this.lockReferenceHolders = !this.minecraftToBukkit.supportsDirectHolders(); + } + + public void lockReferenceHolders() { + Preconditions.checkState(this.cache.isEmpty(), "Registry %s is already loaded", this.minecraftRegistry.key()); + + try { + Class.forName(this.bukkitClass.getName()); // this should always trigger the initialization of the class + } catch (final ClassNotFoundException e) { + throw new IllegalStateException("Failed to load class " + this.bukkitClass.getSimpleName(), e); + } + if (!this.minecraftToBukkit.supportsDirectHolders()) { + return; + } + Preconditions.checkState(!this.lockReferenceHolders, "Reference holders are already locked"); + this.lockReferenceHolders = true; } // Paper - inline into CraftRegistry#get(Registry, NamespacedKey, ApiVersion) above @@ -188,28 +208,19 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> { 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 != <bukkitClass>.<field> 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 == <bukkitClass>.<field> - 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<Holder.Reference<M>> holderOptional = this.minecraftRegistry.get(CraftNamespacedKey.toMinecraft(namespacedKey)); + final Holder.Reference<M> 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; } @@ -285,4 +296,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> { return this.minecraftRegistry.getTags().<Tag<B>>map(NamedRegistryKeySetImpl::new).toList(); } // Paper end - RegistrySet API + + final class InvalidHolderOwner implements HolderOwner<M> { + } } 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<net.minecraft.world.item.enchantment.Enchantment> { +public class CraftEnchantment extends Enchantment implements Holderable<net.minecraft.world.item.enchantment.Enchantment> { 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<net.mine return CraftRegistry.get(Registry.ENCHANTMENT, key, ApiVersion.CURRENT); } - private final NamespacedKey key; private final Holder<net.minecraft.world.item.enchantment.Enchantment> 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<net.minecraft.world.item.enchantment.Enchantment> holder) { + this.handle = holder; } @Override - public net.minecraft.world.item.enchantment.Enchantment getHandle() { - return this.handle.value(); + public Holder<net.minecraft.world.item.enchantment.Enchantment> 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<net.mine @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - - if (!(other instanceof CraftEnchantment)) { - return false; - } - - return this.getKey().equals(((Enchantment) other).getKey()); + return Holderable.super.implEquals(other); } @Override public int hashCode() { - return this.getKey().hashCode(); + return Holderable.super.implHashCode(); } @Override public String toString() { - return "CraftEnchantment[" + this.getKey() + "]"; + return Holderable.super.implToString(); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java index 9b7488cb45..77378a90fa 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java @@ -30,9 +30,6 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.TRIM_MATERIAL); // Paper - switch to Holder } - private final NamespacedKey key; - private final net.minecraft.world.item.equipment.trim.TrimMaterial handle; - // Paper start - switch to Holder private final Holder<net.minecraft.world.item.equipment.trim.TrimMaterial> holder; @@ -64,8 +61,6 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho } public CraftTrimMaterial(final Holder<net.minecraft.world.item.equipment.trim.TrimMaterial> holder) { - this.key = holder.unwrapKey().map(io.papermc.paper.util.MCUtil::fromResourceKey).orElse(null); - this.handle = holder.value(); this.holder = holder; // Paper end - switch to Holder } @@ -84,14 +79,14 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho @NotNull @Override public String getTranslationKey() { - if (!(this.handle.description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper - return ((TranslatableContents) this.handle.description().getContents()).getKey(); + if (!(this.getHandle().description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper + return ((TranslatableContents) this.getHandle().description().getContents()).getKey(); } // Paper start - adventure @Override public net.kyori.adventure.text.Component description() { - return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.handle.description()); + return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getHandle().description()); } // Paper end - adventure } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java index b5b209d16a..0d92f77467 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java @@ -30,9 +30,6 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.TRIM_PATTERN); // Paper - switch to Holder } - private final NamespacedKey key; - private final net.minecraft.world.item.equipment.trim.TrimPattern handle; - // Paper start - switch to Holder private final Holder<net.minecraft.world.item.equipment.trim.TrimPattern> holder; // Paper - switch to Holder @@ -64,8 +61,6 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold } public CraftTrimPattern(Holder<net.minecraft.world.item.equipment.trim.TrimPattern> handle) { - this.key = handle.unwrapKey().map(io.papermc.paper.util.MCUtil::fromResourceKey).orElse(null); - this.handle = handle.value(); this.holder = handle; // Paper end - switch to Holder } @@ -84,14 +79,14 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold @NotNull @Override public String getTranslationKey() { - if (!(this.handle.description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper - return ((TranslatableContents) this.handle.description().getContents()).getKey(); + if (!(this.getHandle().description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper + return ((TranslatableContents) this.getHandle().description().getContents()).getKey(); } // Paper start - adventure @Override public net.kyori.adventure.text.Component description() { - return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.handle.description()); + return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getHandle().description()); } // Paper end - adventure } diff --git a/paper-server/src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java b/paper-server/src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java index 0c704fb9e7..d548ff4a96 100644 --- a/paper-server/src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java +++ b/paper-server/src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java @@ -25,7 +25,7 @@ public class RegistryLoadOrderTest { private static boolean initInterface = false; private static boolean initAbstract = false; - private static org.bukkit.Registry<Keyed> registry; // Paper - remap fix + private static Registry<Keyed> registry; public static Stream<Arguments> data() { return Stream.of( @@ -60,6 +60,7 @@ public class RegistryLoadOrderTest { RegistryLoadOrderTest.registry = new CraftRegistry<>(keyedClass, minecraftRegistry, minecraftToBukkit, (namespacedKey, apiVersion) -> namespacedKey); this.testClassNotLoaded(init.get()); + ((CraftRegistry<?, ?>) RegistryLoadOrderTest.registry).lockReferenceHolders(); Object testOne = RegistryLoadOrderTest.registry.get(new NamespacedKey("bukkit", "test-one")); Object otherTestOne = RegistryLoadOrderTest.registry.get(new NamespacedKey("bukkit", "test-one"));