Add RegistryAccess for managing Registries
RegistryAccess is independant from CraftServer and doesn't require one to be created allowing the org.bukkit.Registry class to be loaded earlier. == AT == public net.minecraft.server.RegistryLayer STATIC_ACCESS
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package io.papermc.paper.registry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.registry.entry.RegistryEntry;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.core.Registry;
|
||||
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.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.CraftArt;
|
||||
import org.bukkit.craftbukkit.CraftFluid;
|
||||
import org.bukkit.craftbukkit.CraftGameEvent;
|
||||
import org.bukkit.craftbukkit.CraftJukeboxSong;
|
||||
import org.bukkit.craftbukkit.CraftMusicInstrument;
|
||||
import org.bukkit.craftbukkit.CraftSound;
|
||||
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.CraftNamespacedKey;
|
||||
import org.bukkit.damage.DamageType;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Cat;
|
||||
import org.bukkit.entity.Frog;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.Wolf;
|
||||
import org.bukkit.entity.memory.MemoryKey;
|
||||
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.jspecify.annotations.Nullable;
|
||||
|
||||
import static io.papermc.paper.registry.entry.RegistryEntryBuilder.start;
|
||||
|
||||
public final class PaperRegistries {
|
||||
|
||||
static final List<RegistryEntry<?, ?>> REGISTRY_ENTRIES;
|
||||
private static final Map<RegistryKey<?>, RegistryEntry<?, ?>> BY_REGISTRY_KEY;
|
||||
private static final Map<ResourceKey<?>, RegistryEntry<?, ?>> BY_RESOURCE_KEY;
|
||||
static {
|
||||
REGISTRY_ENTRIES = List.of(
|
||||
// built-ins
|
||||
start(Registries.GAME_EVENT, RegistryKey.GAME_EVENT).craft(GameEvent.class, CraftGameEvent::new).build(),
|
||||
start(Registries.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE).craft(StructureType.class, CraftStructureType::new).build(),
|
||||
start(Registries.MOB_EFFECT, RegistryKey.MOB_EFFECT).craft(PotionEffectType.class, CraftPotionEffectType::new).build(),
|
||||
start(Registries.BLOCK, RegistryKey.BLOCK).craft(BlockType.class, CraftBlockType::new).build(),
|
||||
start(Registries.ITEM, RegistryKey.ITEM).craft(ItemType.class, CraftItemType::new).build(),
|
||||
start(Registries.CAT_VARIANT, RegistryKey.CAT_VARIANT).craft(Cat.Type.class, CraftCat.CraftType::new).build(),
|
||||
start(Registries.FROG_VARIANT, RegistryKey.FROG_VARIANT).craft(Frog.Variant.class, CraftFrog.CraftVariant::new).build(),
|
||||
start(Registries.VILLAGER_PROFESSION, RegistryKey.VILLAGER_PROFESSION).craft(Villager.Profession.class, CraftVillager.CraftProfession::new).build(),
|
||||
start(Registries.VILLAGER_TYPE, RegistryKey.VILLAGER_TYPE).craft(Villager.Type.class, CraftVillager.CraftType::new).build(),
|
||||
start(Registries.MAP_DECORATION_TYPE, RegistryKey.MAP_DECORATION_TYPE).craft(MapCursor.Type.class, CraftMapCursor.CraftType::new).build(),
|
||||
start(Registries.MENU, RegistryKey.MENU).craft(MenuType.class, CraftMenuType::new).build(),
|
||||
start(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE).craft(Attribute.class, CraftAttribute::new).build(),
|
||||
start(Registries.FLUID, RegistryKey.FLUID).craft(Fluid.class, CraftFluid::new).build(),
|
||||
start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new).build(),
|
||||
|
||||
// data-drivens
|
||||
start(Registries.BIOME, RegistryKey.BIOME).craft(Biome.class, CraftBiome::new).build().delayed(),
|
||||
start(Registries.STRUCTURE, RegistryKey.STRUCTURE).craft(Structure.class, CraftStructure::new).build().delayed(),
|
||||
start(Registries.TRIM_MATERIAL, RegistryKey.TRIM_MATERIAL).craft(TrimMaterial.class, CraftTrimMaterial::new).build().delayed(),
|
||||
start(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN).craft(TrimPattern.class, CraftTrimPattern::new).build().delayed(),
|
||||
start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).build().delayed(),
|
||||
start(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT).craft(Wolf.Variant.class, CraftWolf.CraftVariant::new).build().delayed(),
|
||||
start(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT).craft(Enchantment.class, CraftEnchantment::new).build().withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
|
||||
start(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG).craft(JukeboxSong.class, CraftJukeboxSong::new).build().delayed(),
|
||||
start(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN).craft(PatternType.class, CraftPatternType::new).build().delayed(),
|
||||
start(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT).craft(Art.class, CraftArt::new).build().delayed(),
|
||||
start(Registries.INSTRUMENT, RegistryKey.INSTRUMENT).craft(MusicInstrument.class, CraftMusicInstrument::new).build().delayed(),
|
||||
|
||||
// api-only
|
||||
start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType),
|
||||
start(Registries.PARTICLE_TYPE, RegistryKey.PARTICLE_TYPE).apiOnly(PaperSimpleRegistry::particleType),
|
||||
start(Registries.POTION, RegistryKey.POTION).apiOnly(PaperSimpleRegistry::potion),
|
||||
start(Registries.MEMORY_MODULE_TYPE, RegistryKey.MEMORY_MODULE_TYPE).apiOnly(() -> (org.bukkit.Registry<MemoryKey<?>>) (org.bukkit.Registry) org.bukkit.Registry.MEMORY_MODULE_TYPE)
|
||||
);
|
||||
final Map<RegistryKey<?>, RegistryEntry<?, ?>> byRegistryKey = new IdentityHashMap<>(REGISTRY_ENTRIES.size());
|
||||
final Map<ResourceKey<?>, RegistryEntry<?, ?>> byResourceKey = new IdentityHashMap<>(REGISTRY_ENTRIES.size());
|
||||
for (final RegistryEntry<?, ?> entry : REGISTRY_ENTRIES) {
|
||||
Preconditions.checkState(byRegistryKey.put(entry.apiKey(), entry) == null, "Duplicate api registry key: %s", entry.apiKey());
|
||||
Preconditions.checkState(byResourceKey.put(entry.mcKey(), entry) == null, "Duplicate mc registry key: %s", entry.mcKey());
|
||||
}
|
||||
BY_REGISTRY_KEY = Collections.unmodifiableMap(byRegistryKey);
|
||||
BY_RESOURCE_KEY = Collections.unmodifiableMap(byResourceKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <M, T extends Keyed> @Nullable RegistryEntry<M, T> getEntry(final ResourceKey<? extends Registry<M>> resourceKey) {
|
||||
return (RegistryEntry<M, T>) BY_RESOURCE_KEY.get(resourceKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <M, T extends Keyed> @Nullable RegistryEntry<M, T> getEntry(final RegistryKey<? super T> registryKey) {
|
||||
return (RegistryEntry<M, T>) BY_REGISTRY_KEY.get(registryKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <M, T> RegistryKey<T> registryFromNms(final ResourceKey<? extends Registry<M>> registryResourceKey) {
|
||||
return (RegistryKey<T>) Objects.requireNonNull(BY_RESOURCE_KEY.get(registryResourceKey), registryResourceKey + " doesn't have an api RegistryKey").apiKey();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <M, T> ResourceKey<? extends Registry<M>> registryToNms(final RegistryKey<T> registryKey) {
|
||||
return (ResourceKey<? extends Registry<M>>) Objects.requireNonNull(BY_REGISTRY_KEY.get(registryKey), registryKey + " doesn't have an mc registry ResourceKey").mcKey();
|
||||
}
|
||||
|
||||
public static <M, T> TypedKey<T> fromNms(final ResourceKey<M> resourceKey) {
|
||||
return TypedKey.create(registryFromNms(resourceKey.registryKey()), CraftNamespacedKey.fromMinecraft(resourceKey.location()));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "RedundantCast"})
|
||||
public static <M, T> ResourceKey<M> toNms(final TypedKey<T> typedKey) {
|
||||
return ResourceKey.create((ResourceKey<? extends Registry<M>>) PaperRegistries.registryToNms(typedKey.registryKey()), PaperAdventure.asVanilla(typedKey.key()));
|
||||
}
|
||||
|
||||
private PaperRegistries() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package io.papermc.paper.registry;
|
||||
|
||||
import io.papermc.paper.registry.entry.ApiRegistryEntry;
|
||||
import io.papermc.paper.registry.entry.RegistryEntry;
|
||||
import io.papermc.paper.registry.legacy.DelayedRegistry;
|
||||
import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
|
||||
import io.papermc.paper.registry.legacy.LegacyRegistryIdentifiers;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Registry;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class PaperRegistryAccess implements RegistryAccess {
|
||||
|
||||
// We store the API registries in a memoized supplier, so they can be created on-demand.
|
||||
// These suppliers are added to this map right after the instance of nms.Registry is created before it is loaded.
|
||||
// We want to do registration there, so we have access to the nms.Registry instance in order to wrap it in a CraftRegistry instance.
|
||||
// The memoized Supplier is needed because we *can't* instantiate any CraftRegistry class until **all** the BuiltInRegistries have been added
|
||||
// to this map because that would class-load org.bukkit.Registry which would query this map.
|
||||
private final Map<RegistryKey<?>, RegistryHolder<?>> registries = new ConcurrentHashMap<>(); // is "identity" because RegistryKey overrides equals and hashCode
|
||||
|
||||
public static PaperRegistryAccess instance() {
|
||||
return (PaperRegistryAccess) RegistryAccessHolder.INSTANCE.orElseThrow(() -> new IllegalStateException("No RegistryAccess implementation found"));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Set<RegistryKey<?>> getLoadedServerBackedRegistries() {
|
||||
return this.registries.keySet().stream().filter(registryHolder -> !(PaperRegistries.getEntry(registryHolder) instanceof ApiRegistryEntry)).collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Deprecated(forRemoval = true)
|
||||
@Override
|
||||
public <T extends Keyed> @Nullable Registry<T> getRegistry(final Class<T> type) {
|
||||
final RegistryKey<T> registryKey = byType(type);
|
||||
// If our mapping from Class -> RegistryKey did not contain the passed type it was either a completely invalid type or a registry
|
||||
// that merely exists as a SimpleRegistry in the org.bukkit.Registry type. We cannot return a registry for these, return null
|
||||
// as per method contract in Bukkit#getRegistry.
|
||||
if (registryKey == null) return null;
|
||||
|
||||
final RegistryEntry<?, T> entry = PaperRegistries.getEntry(registryKey);
|
||||
final RegistryHolder<T> registry = (RegistryHolder<T>) this.registries.get(registryKey);
|
||||
if (registry != null) {
|
||||
// if the registry exists, return right away. Since this is the "legacy" method, we return DelayedRegistry
|
||||
// for the non-builtin Registry instances stored as fields in Registry.
|
||||
return registry.get();
|
||||
} else if (entry instanceof DelayedRegistryEntry<?, T>) {
|
||||
// if the registry doesn't exist and the entry is marked as "delayed", we create a registry holder that is empty
|
||||
// which will later be filled with the actual registry. This is so the fields on org.bukkit.Registry can be populated with
|
||||
// registries that don't exist at the time org.bukkit.Registry is statically initialized.
|
||||
final RegistryHolder<T> delayedHolder = new RegistryHolder.Delayed<>();
|
||||
this.registries.put(registryKey, delayedHolder);
|
||||
return delayedHolder.get();
|
||||
} else {
|
||||
// if the registry doesn't exist yet or doesn't have a delayed entry, just return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends Keyed> Registry<T> getRegistry(final RegistryKey<T> key) {
|
||||
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);
|
||||
if (registryHolder == null) {
|
||||
throw new IllegalArgumentException(key + " points to a registry that is not available yet");
|
||||
}
|
||||
// since this is the getRegistry method that uses the modern RegistryKey, we unwrap any DelayedRegistry instances
|
||||
// that might be returned here. I don't think reference equality is required when doing getRegistry(RegistryKey.WOLF_VARIANT) == Registry.WOLF_VARIANT
|
||||
return possiblyUnwrap(registryHolder.get());
|
||||
}
|
||||
|
||||
private static <T extends Keyed> Registry<T> possiblyUnwrap(final Registry<T> registry) {
|
||||
if (registry instanceof final DelayedRegistry<T, ?> delayedRegistry) { // if not coming from legacy, unwrap the delayed registry
|
||||
return delayedRegistry.delegate();
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
public <M> void registerReloadableRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry) {
|
||||
this.registerRegistry(resourceKey, registry, true);
|
||||
}
|
||||
|
||||
public <M> void registerRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry) {
|
||||
this.registerRegistry(resourceKey, registry, false);
|
||||
}
|
||||
|
||||
@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);
|
||||
if (entry == null) { // skip registries that don't have API entries
|
||||
return;
|
||||
}
|
||||
final @Nullable 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));
|
||||
} else {
|
||||
if (registryHolder instanceof RegistryHolder.Delayed<?, ?> && entry instanceof final DelayedRegistryEntry<M, B> delayedEntry) {
|
||||
// if the registry holder is delayed, and the entry is marked as "delayed", then load the holder with the CraftRegistry instance that wraps the actual nms Registry.
|
||||
((RegistryHolder.Delayed<B, R>) registryHolder).loadFrom(delayedEntry, registry);
|
||||
} else {
|
||||
throw new IllegalArgumentException(resourceKey + " has already been created");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public static <T extends Keyed> @Nullable RegistryKey<T> byType(final Class<T> type) {
|
||||
return (RegistryKey<T>) LegacyRegistryIdentifiers.CLASS_TO_KEY_MAP.get(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.papermc.paper.registry;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.potion.PotionType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class PaperSimpleRegistry<T extends Enum<T> & Keyed, M> extends Registry.SimpleRegistry<T> {
|
||||
|
||||
static Registry<EntityType> entityType() {
|
||||
return new PaperSimpleRegistry<>(EntityType.class, entity -> entity != EntityType.UNKNOWN, BuiltInRegistries.ENTITY_TYPE);
|
||||
}
|
||||
|
||||
static Registry<Particle> particleType() {
|
||||
return new PaperSimpleRegistry<>(Particle.class, BuiltInRegistries.PARTICLE_TYPE);
|
||||
}
|
||||
|
||||
static Registry<PotionType> potion() {
|
||||
return new PaperSimpleRegistry<>(PotionType.class, BuiltInRegistries.POTION);
|
||||
}
|
||||
|
||||
private final net.minecraft.core.Registry<M> nmsRegistry;
|
||||
|
||||
protected PaperSimpleRegistry(final Class<T> type, final net.minecraft.core.Registry<M> nmsRegistry) {
|
||||
super(type);
|
||||
this.nmsRegistry = nmsRegistry;
|
||||
}
|
||||
|
||||
public PaperSimpleRegistry(final Class<T> type, final Predicate<T> predicate, final net.minecraft.core.Registry<M> nmsRegistry) {
|
||||
super(type, predicate);
|
||||
this.nmsRegistry = nmsRegistry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.papermc.paper.registry;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import io.papermc.paper.registry.legacy.DelayedRegistry;
|
||||
import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
|
||||
import java.util.function.Supplier;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Registry;
|
||||
|
||||
public interface RegistryHolder<B extends Keyed> {
|
||||
|
||||
Registry<B> get();
|
||||
|
||||
final class Memoized<B extends Keyed, R extends Registry<B>> implements RegistryHolder<B> {
|
||||
|
||||
private final Supplier<R> memoizedSupplier;
|
||||
|
||||
public Memoized(final Supplier<? extends R> supplier) {
|
||||
this.memoizedSupplier = Suppliers.memoize(supplier::get);
|
||||
}
|
||||
|
||||
public Registry<B> get() {
|
||||
return this.memoizedSupplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
final class Delayed<B extends Keyed, R extends Registry<B>> implements RegistryHolder<B> {
|
||||
|
||||
private final DelayedRegistry<B, R> delayedRegistry = new DelayedRegistry<>();
|
||||
|
||||
@Override
|
||||
public DelayedRegistry<B, R> get() {
|
||||
return this.delayedRegistry;
|
||||
}
|
||||
|
||||
<M> void loadFrom(final DelayedRegistryEntry<M, B> delayedEntry, final net.minecraft.core.Registry<M> registry) {
|
||||
final RegistryHolder<B> delegateHolder = delayedEntry.delegate().createRegistryHolder(registry);
|
||||
if (!(delegateHolder instanceof RegistryHolder.Memoized<B, ?>)) {
|
||||
throw new IllegalArgumentException(delegateHolder + " must be a memoized holder");
|
||||
}
|
||||
this.delayedRegistry.load(((Memoized<B, R>) delegateHolder).memoizedSupplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import io.papermc.paper.registry.RegistryHolder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
|
||||
public class ApiRegistryEntry<M, B extends Keyed> extends BaseRegistryEntry<M, B> {
|
||||
|
||||
private final Supplier<org.bukkit.Registry<B>> registrySupplier;
|
||||
|
||||
protected ApiRegistryEntry(
|
||||
final ResourceKey<? extends Registry<M>> mcKey,
|
||||
final RegistryKey<B> apiKey,
|
||||
final Supplier<org.bukkit.Registry<B>> registrySupplier
|
||||
) {
|
||||
super(mcKey, apiKey);
|
||||
this.registrySupplier = registrySupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryHolder<B> createRegistryHolder(final Registry<M> nmsRegistry) {
|
||||
return new RegistryHolder.Memoized<>(this.registrySupplier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
|
||||
public abstract class BaseRegistryEntry<M, B extends Keyed> implements RegistryEntry<M, B> { // TODO remove Keyed
|
||||
|
||||
private final ResourceKey<? extends Registry<M>> minecraftRegistryKey;
|
||||
private final RegistryKey<B> apiRegistryKey;
|
||||
|
||||
protected BaseRegistryEntry(final ResourceKey<? extends Registry<M>> minecraftRegistryKey, final RegistryKey<B> apiRegistryKey) {
|
||||
this.minecraftRegistryKey = minecraftRegistryKey;
|
||||
this.apiRegistryKey = apiRegistryKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResourceKey<? extends Registry<M>> mcKey() {
|
||||
return this.minecraftRegistryKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final RegistryKey<B> apiKey() {
|
||||
return this.apiRegistryKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import io.papermc.paper.registry.RegistryHolder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.bukkit.craftbukkit.util.ApiVersion;
|
||||
|
||||
public class CraftRegistryEntry<M, B extends Keyed> extends BaseRegistryEntry<M, B> { // TODO remove Keyed
|
||||
|
||||
private static final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> EMPTY = (namespacedKey, apiVersion) -> namespacedKey;
|
||||
|
||||
protected final Class<?> classToPreload;
|
||||
protected final RegistryTypeMapper<M, B> minecraftToBukkit;
|
||||
protected BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater = EMPTY;
|
||||
|
||||
protected CraftRegistryEntry(
|
||||
final ResourceKey<? extends Registry<M>> mcKey,
|
||||
final RegistryKey<B> apiKey,
|
||||
final Class<?> classToPreload,
|
||||
final RegistryTypeMapper<M, B> minecraftToBukkit
|
||||
) {
|
||||
super(mcKey, apiKey);
|
||||
Preconditions.checkArgument(!classToPreload.getPackageName().startsWith("net.minecraft"), classToPreload + " should not be in the net.minecraft package as the class-to-preload");
|
||||
this.classToPreload = classToPreload;
|
||||
this.minecraftToBukkit = minecraftToBukkit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryEntry<M, B> withSerializationUpdater(final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater) {
|
||||
this.updater = updater;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryHolder<B> createRegistryHolder(final Registry<M> nmsRegistry) {
|
||||
return new RegistryHolder.Memoized<>(() -> this.createApiRegistry(nmsRegistry));
|
||||
}
|
||||
|
||||
private CraftRegistry<B, M> createApiRegistry(final Registry<M> nmsRegistry) {
|
||||
return new CraftRegistry<>(this.classToPreload, nmsRegistry, this.minecraftToBukkit, this.updater);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import io.papermc.paper.registry.RegistryHolder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
|
||||
import java.util.function.BiFunction;
|
||||
import net.minecraft.core.Registry;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.craftbukkit.util.ApiVersion;
|
||||
|
||||
public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M, B> { // TODO remove Keyed
|
||||
|
||||
RegistryHolder<B> createRegistryHolder(Registry<M> nmsRegistry);
|
||||
|
||||
default RegistryEntry<M, B> withSerializationUpdater(final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should only be used if the registry instance needs to exist early due to the need
|
||||
* to populate a field in {@link org.bukkit.Registry}. Data-driven registries shouldn't exist
|
||||
* as fields, but instead be obtained via {@link io.papermc.paper.registry.RegistryAccess#getRegistry(RegistryKey)}
|
||||
*/
|
||||
@Deprecated
|
||||
default RegistryEntry<M, B> delayed() {
|
||||
return new DelayedRegistryEntry<>(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
||||
public class RegistryEntryBuilder<M, A extends Keyed> { // TODO remove Keyed
|
||||
|
||||
public static <M, A extends Keyed> RegistryEntryBuilder<M, A> start( // TODO remove Keyed
|
||||
final ResourceKey<? extends Registry<M>> mcKey,
|
||||
final RegistryKey<A> apiKey
|
||||
) {
|
||||
return new RegistryEntryBuilder<>(mcKey, apiKey);
|
||||
}
|
||||
|
||||
protected final ResourceKey<? extends Registry<M>> mcKey;
|
||||
protected final RegistryKey<A> apiKey;
|
||||
|
||||
private RegistryEntryBuilder(final ResourceKey<? extends Registry<M>> mcKey, RegistryKey<A> apiKey) {
|
||||
this.mcKey = mcKey;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public RegistryEntry<M, A> apiOnly(final Supplier<org.bukkit.Registry<A>> apiRegistrySupplier) {
|
||||
return new ApiRegistryEntry<>(this.mcKey, this.apiKey, apiRegistrySupplier);
|
||||
}
|
||||
|
||||
public CraftStage<M, A> craft(final Class<?> classToPreload, final BiFunction<? super NamespacedKey, M, ? extends A> minecraftToBukkit) {
|
||||
return new CraftStage<>(this.mcKey, this.apiKey, classToPreload, new RegistryTypeMapper<>(minecraftToBukkit));
|
||||
}
|
||||
|
||||
public CraftStage<M, A> craft(final Class<?> classToPreload, final Function<Holder<M>, ? extends A> minecraftToBukkit) {
|
||||
return new CraftStage<>(this.mcKey, this.apiKey, classToPreload, new RegistryTypeMapper<>(minecraftToBukkit));
|
||||
}
|
||||
|
||||
public static final class CraftStage<M, A extends Keyed> extends RegistryEntryBuilder<M, A> { // TODO remove Keyed
|
||||
|
||||
private final Class<?> classToPreload;
|
||||
private final RegistryTypeMapper<M, A> minecraftToBukkit;
|
||||
|
||||
private CraftStage(
|
||||
final ResourceKey<? extends Registry<M>> mcKey,
|
||||
final RegistryKey<A> apiKey,
|
||||
final Class<?> classToPreload,
|
||||
final RegistryTypeMapper<M, A> minecraftToBukkit
|
||||
) {
|
||||
super(mcKey, apiKey);
|
||||
this.classToPreload = classToPreload;
|
||||
this.minecraftToBukkit = minecraftToBukkit;
|
||||
}
|
||||
|
||||
public RegistryEntry<M, A> build() {
|
||||
return new CraftRegistryEntry<>(this.mcKey, this.apiKey, this.classToPreload, this.minecraftToBukkit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
|
||||
public interface RegistryEntryInfo<M, B> {
|
||||
|
||||
ResourceKey<? extends Registry<M>> mcKey();
|
||||
|
||||
RegistryKey<B> apiKey();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import net.minecraft.core.Holder;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
||||
public final class RegistryTypeMapper<M, A> {
|
||||
|
||||
final Either<BiFunction<? super NamespacedKey, M, ? extends A>, Function<Holder<M>, ? extends A>> minecraftToBukkit;
|
||||
|
||||
public RegistryTypeMapper(final BiFunction<? super NamespacedKey, M, ? extends A> byValueCreator) {
|
||||
this.minecraftToBukkit = Either.left(byValueCreator);
|
||||
}
|
||||
|
||||
public RegistryTypeMapper(final Function<Holder<M>, ? extends A> byHolderCreator) {
|
||||
this.minecraftToBukkit = Either.right(byHolderCreator);
|
||||
}
|
||||
|
||||
public A createBukkit(final NamespacedKey key, final Holder<M> minecraft) {
|
||||
return this.minecraftToBukkit.map(
|
||||
minecraftToBukkit -> minecraftToBukkit.apply(key, minecraft.value()),
|
||||
minecraftToBukkit -> minecraftToBukkit.apply(minecraft)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean supportsDirectHolders() {
|
||||
return this.minecraftToBukkit.right().isPresent();
|
||||
}
|
||||
|
||||
public A convertDirectHolder(final Holder<M> directHolder) {
|
||||
Preconditions.checkArgument(this.supportsDirectHolders() && directHolder.kind() == Holder.Kind.DIRECT);
|
||||
return this.minecraftToBukkit.right().orElseThrow().apply(directHolder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.registry.entry;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.papermc.paper.registry.legacy;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* This is to support the now-deprecated fields in {@link Registry} for
|
||||
* data-driven registries.
|
||||
*/
|
||||
public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> implements Registry<T> {
|
||||
|
||||
private @Nullable Supplier<? extends R> delegate;
|
||||
|
||||
public void load(final Supplier<? extends R> registry) {
|
||||
if (this.delegate != null) {
|
||||
throw new IllegalStateException("Registry already loaded!");
|
||||
}
|
||||
this.delegate = registry;
|
||||
}
|
||||
|
||||
public Registry<T> delegate() {
|
||||
if (this.delegate == null) {
|
||||
throw new IllegalStateException("You are trying to access this registry too early!");
|
||||
}
|
||||
return this.delegate.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T get(final NamespacedKey key) {
|
||||
return this.delegate().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getOrThrow(final NamespacedKey key) {
|
||||
return this.delegate().getOrThrow(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return this.delegate().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return this.delegate().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable NamespacedKey getKey(final T value) {
|
||||
return this.delegate().getKey(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.papermc.paper.registry.legacy;
|
||||
|
||||
import io.papermc.paper.registry.RegistryHolder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.entry.RegistryEntry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
|
||||
public record DelayedRegistryEntry<M, T extends Keyed>(RegistryEntry<M, T> delegate) implements RegistryEntry<M, T> {
|
||||
|
||||
@Override
|
||||
public ResourceKey<? extends Registry<M>> mcKey() {
|
||||
return this.delegate.mcKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryKey<T> apiKey() {
|
||||
return this.delegate.apiKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryHolder<T> createRegistryHolder(final Registry<M> nmsRegistry) {
|
||||
return this.delegate.createRegistryHolder(nmsRegistry);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.papermc.paper.registry.legacy;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.leangen.geantyref.GenericTypeReflector;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Map;
|
||||
|
||||
@Deprecated
|
||||
public final class LegacyRegistryIdentifiers {
|
||||
|
||||
public static final Map<Class<?>, RegistryKey<?>> CLASS_TO_KEY_MAP;
|
||||
|
||||
static {
|
||||
final ImmutableMap.Builder<Class<?>, RegistryKey<?>> builder = ImmutableMap.builder();
|
||||
try {
|
||||
for (final Field field : RegistryKey.class.getFields()) {
|
||||
if (field.getType() == RegistryKey.class) {
|
||||
// get the legacy type from the RegistryKey generic parameter on the field
|
||||
final Class<?> legacyType = GenericTypeReflector.erase(((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]);
|
||||
builder.put(legacyType, (RegistryKey<?>) field.get(null));
|
||||
}
|
||||
}
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
CLASS_TO_KEY_MAP = builder.build();
|
||||
}
|
||||
|
||||
private LegacyRegistryIdentifiers() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.registry.legacy;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.registry;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.papermc.paper.util;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import org.bukkit.craftbukkit.util.Handleable;
|
||||
|
||||
public interface Holderable<M> extends Handleable<M> {
|
||||
|
||||
Holder<M> getHolder();
|
||||
|
||||
@Override
|
||||
default M getHandle() {
|
||||
return this.getHolder().value();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user