package org.bukkit.craftbukkit; import com.google.common.base.Preconditions; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.function.BiFunction; import java.util.stream.Stream; import net.minecraft.core.Holder; 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 { private static RegistryAccess registry; public static void setMinecraftRegistry(RegistryAccess registry) { Preconditions.checkState(CraftRegistry.registry == null, "Registry already set"); CraftRegistry.registry = registry; } public static RegistryAccess getMinecraftRegistry() { return CraftRegistry.registry; } public static net.minecraft.core.Registry getMinecraftRegistry(ResourceKey> key) { return CraftRegistry.getMinecraftRegistry().lookupOrThrow(key); } /** * Usage note: Only use this method to delegate the conversion methods from the individual Craft classes to here. * Do not use it in other parts of CraftBukkit, use the methods in the respective Craft classes instead. * * @param minecraft the minecraft representation * @param registryKey the registry key of the minecraft registry to use * @param bukkitRegistry the bukkit registry to use * @return the bukkit representation of the minecraft value */ public static B minecraftToBukkit(M minecraft, ResourceKey> registryKey, Registry bukkitRegistry) { Preconditions.checkArgument(minecraft != null); net.minecraft.core.Registry registry = CraftRegistry.getMinecraftRegistry(registryKey); // Paper start - support direct Holders final java.util.Optional> resourceKey = registry.getResourceKey(minecraft); if (resourceKey.isEmpty() && bukkitRegistry instanceof final CraftRegistry craftRegistry && craftRegistry.supportsDirectHolders()) { return ((CraftRegistry) registry).convertDirectHolder(Holder.direct(minecraft)); } else if (resourceKey.isEmpty()) { throw new IllegalStateException(String.format("Cannot convert '%s' to bukkit representation, since it is not registered.", minecraft)); } final B bukkit = bukkitRegistry.get(CraftNamespacedKey.fromMinecraft(resourceKey.get().location())); // Paper end - support direct Holders Preconditions.checkArgument(bukkit != null); return bukkit; } // Paper start - support direct Holders public static B minecraftHolderToBukkit(final Holder minecraft, final Registry bukkitRegistry) { Preconditions.checkArgument(minecraft != null); final B bukkit = switch (minecraft) { case final Holder.Direct direct -> { if (!(bukkitRegistry instanceof final CraftRegistry craftRegistry) || !craftRegistry.supportsDirectHolders()) { throw new IllegalArgumentException("Cannot convert direct holder to bukkit representation"); } yield ((CraftRegistry) bukkitRegistry).convertDirectHolder(direct); } case final Holder.Reference reference -> bukkitRegistry.get(io.papermc.paper.util.MCUtil.fromResourceKey(reference.key())); default -> throw new IllegalArgumentException("Unknown holder: " + minecraft); }; Preconditions.checkArgument(bukkit != null); return bukkit; } // Paper end - support direct Holders /** * Usage note: Only use this method to delegate the conversion methods from the individual Craft classes to here. * Do not use it in other parts of CraftBukkit, use the methods in the respective Craft classes instead. * * @param bukkit the bukkit representation * @return the minecraft representation of the bukkit value */ public static M bukkitToMinecraft(B bukkit) { Preconditions.checkArgument(bukkit != null); return ((Handleable) bukkit).getHandle(); } public static Holder bukkitToMinecraftHolder(B bukkit, ResourceKey> registryKey) { Preconditions.checkArgument(bukkit != null); // Paper start - support direct Holder if (bukkit instanceof io.papermc.paper.util.Holderable) { return ((io.papermc.paper.util.Holderable) bukkit).getHolder(); } // Paper end - support direct Holder net.minecraft.core.Registry registry = CraftRegistry.getMinecraftRegistry(registryKey); if (registry.wrapAsHolder(CraftRegistry.bukkitToMinecraft(bukkit)) instanceof Holder.Reference holder) { return holder; } throw new IllegalArgumentException("No Reference holder found for " + bukkit + ", this can happen if a plugin creates its own registry entry with out properly registering it."); } // Paper - move to PaperRegistries // Paper - NOTE: As long as all uses of the method below relate to *serialization* via ConfigurationSerializable, it's fine public static B get(Registry bukkit, NamespacedKey namespacedKey, ApiVersion apiVersion) { if (bukkit instanceof CraftRegistry craft) { return craft.get(craft.serializationUpdater.apply(namespacedKey, apiVersion)); // Paper } if (bukkit instanceof Registry.SimpleRegistry simple) { Class bClass = simple.getType(); if (bClass == EntityType.class) { return bukkit.get(FieldRename.ENTITY_TYPE_RENAME.apply(namespacedKey, apiVersion)); } if (bClass == Particle.class) { return bukkit.get(FieldRename.PARTICLE_TYPE_RENAME.apply(namespacedKey, apiVersion)); } } return bukkit.get(namespacedKey); } private final Class bukkitClass; // Paper - relax preload class private final Map cache = new HashMap<>(); 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; public CraftRegistry(Class bukkitClass, net.minecraft.core.Registry minecraftRegistry, BiFunction minecraftToBukkit, BiFunction serializationUpdater) { // Paper - relax preload class // Paper start - switch to Holder this(bukkitClass, minecraftRegistry, new io.papermc.paper.registry.entry.RegistryTypeMapper<>(minecraftToBukkit), serializationUpdater); } public CraftRegistry(Class bukkitClass, net.minecraft.core.Registry minecraftRegistry, io.papermc.paper.registry.entry.RegistryTypeMapper minecraftToBukkit, BiFunction serializationUpdater) { // Paper - relax preload class // Paper end - support Holders this.bukkitClass = bukkitClass; this.minecraftRegistry = minecraftRegistry; this.minecraftToBukkit = minecraftToBukkit; this.serializationUpdater = serializationUpdater; } // Paper - inline into CraftRegistry#get(Registry, NamespacedKey, ApiVersion) above @Override public B get(NamespacedKey namespacedKey) { B cached = this.cache.get(namespacedKey); if (cached != null) { 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); } B bukkit = this.createBukkit(namespacedKey, this.minecraftRegistry.get(CraftNamespacedKey.toMinecraft(namespacedKey)).orElse(null)); // Paper - switch to Holder if (bukkit == null) { return null; } this.cache.put(namespacedKey, bukkit); return bukkit; } @NotNull @Override public B getOrThrow(@NotNull NamespacedKey namespacedKey) { B object = this.get(namespacedKey); Preconditions.checkArgument(object != null, "No %s registry entry found for key %s.", this.minecraftRegistry.key(), namespacedKey); return object; } @NotNull @Override public Stream stream() { return this.minecraftRegistry.keySet().stream().map(minecraftKey -> this.get(CraftNamespacedKey.fromMinecraft(minecraftKey))); } @Override public Iterator iterator() { return this.stream().iterator(); } public B createBukkit(NamespacedKey namespacedKey, Holder minecraft) { // Paper - switch to Holder if (minecraft == null) { return null; } return this.minecraftToBukkit.createBukkit(namespacedKey, minecraft); // Paper - switch to Holder } // Paper start - support Direct Holders public boolean supportsDirectHolders() { return this.minecraftToBukkit.supportsDirectHolders(); } public B convertDirectHolder(Holder holder) { return this.minecraftToBukkit.convertDirectHolder(holder); } // Paper end - support Direct Holders }