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.IRegistry; import net.minecraft.core.IRegistryCustom; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import org.bukkit.GameEvent; import org.bukkit.Keyed; import org.bukkit.MusicInstrument; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.damage.CraftDamageType; import org.bukkit.craftbukkit.enchantments.CraftEnchantment; import org.bukkit.craftbukkit.generator.structure.CraftStructure; import org.bukkit.craftbukkit.generator.structure.CraftStructureType; import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern; import org.bukkit.craftbukkit.potion.CraftPotionEffectType; 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.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; public class CraftRegistry implements Registry { private static IRegistryCustom registry; public static void setMinecraftRegistry(IRegistryCustom registry) { Preconditions.checkState(CraftRegistry.registry == null, "Registry already set"); CraftRegistry.registry = registry; } public static IRegistryCustom getMinecraftRegistry() { return registry; } public static IRegistry getMinecraftRegistry(ResourceKey> key) { return getMinecraftRegistry().registryOrThrow(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); IRegistry registry = CraftRegistry.getMinecraftRegistry(registryKey); B bukkit = bukkitRegistry.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft) .orElseThrow(() -> new IllegalStateException(String.format("Cannot convert '%s' to bukkit representation, since it is not registered.", minecraft))).location())); Preconditions.checkArgument(bukkit != null); return bukkit; } /** * 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(); } /** * Note: Newly added registries should also be added to RegistriesArgumentProvider in the test package * * @param bukkitClass the bukkit class of the registry * @param registryHolder the minecraft registry holder * @return the bukkit registry of the provided class */ public static Registry createRegistry(Class bukkitClass, IRegistryCustom registryHolder) { if (bukkitClass == Enchantment.class) { return new CraftRegistry<>(Enchantment.class, registryHolder.registryOrThrow(Registries.ENCHANTMENT), CraftEnchantment::new); } if (bukkitClass == GameEvent.class) { return new CraftRegistry<>(GameEvent.class, registryHolder.registryOrThrow(Registries.GAME_EVENT), CraftGameEvent::new); } if (bukkitClass == MusicInstrument.class) { return new CraftRegistry<>(MusicInstrument.class, registryHolder.registryOrThrow(Registries.INSTRUMENT), CraftMusicInstrument::new); } if (bukkitClass == PotionEffectType.class) { return new CraftRegistry<>(PotionEffectType.class, registryHolder.registryOrThrow(Registries.MOB_EFFECT), CraftPotionEffectType::new); } if (bukkitClass == Structure.class) { return new CraftRegistry<>(Structure.class, registryHolder.registryOrThrow(Registries.STRUCTURE), CraftStructure::new); } if (bukkitClass == StructureType.class) { return new CraftRegistry<>(StructureType.class, BuiltInRegistries.STRUCTURE_TYPE, CraftStructureType::new); } if (bukkitClass == TrimMaterial.class) { return new CraftRegistry<>(TrimMaterial.class, registryHolder.registryOrThrow(Registries.TRIM_MATERIAL), CraftTrimMaterial::new); } if (bukkitClass == TrimPattern.class) { return new CraftRegistry<>(TrimPattern.class, registryHolder.registryOrThrow(Registries.TRIM_PATTERN), CraftTrimPattern::new); } if (bukkitClass == DamageType.class) { return new CraftRegistry<>(DamageType.class, registryHolder.registryOrThrow(Registries.DAMAGE_TYPE), CraftDamageType::new); } return null; } private final Class bukkitClass; private final Map cache = new HashMap<>(); private final IRegistry minecraftRegistry; private final BiFunction minecraftToBukkit; private boolean init; public CraftRegistry(Class bukkitClass, IRegistry minecraftRegistry, BiFunction minecraftToBukkit) { this.bukkitClass = bukkitClass; this.minecraftRegistry = minecraftRegistry; this.minecraftToBukkit = minecraftToBukkit; } @Override public B get(NamespacedKey namespacedKey) { B cached = 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 (!init) { init = true; try { Class.forName(bukkitClass.getName()); } catch (ClassNotFoundException e) { throw new RuntimeException("Could not load registry class " + bukkitClass, e); } return get(namespacedKey); } B bukkit = createBukkit(namespacedKey, minecraftRegistry.getOptional(CraftNamespacedKey.toMinecraft(namespacedKey)).orElse(null)); if (bukkit == null) { return null; } cache.put(namespacedKey, bukkit); return bukkit; } @NotNull @Override public Stream stream() { return minecraftRegistry.keySet().stream().map(minecraftKey -> get(CraftNamespacedKey.fromMinecraft(minecraftKey))); } @Override public Iterator iterator() { return stream().iterator(); } public B createBukkit(NamespacedKey namespacedKey, M minecraft) { if (minecraft == null) { return null; } return minecraftToBukkit.apply(namespacedKey, minecraft); } }