b3ac8604eb
This fixes a registry load order issue, which could cause that two different instances of the same registry item could exist, when the Bukkit class was not loaded before an item was queried. By: DerFrZocker <derrieple@gmail.com>
139 lines
5.4 KiB
Java
139 lines
5.4 KiB
Java
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.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.util.CraftNamespacedKey;
|
|
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.jetbrains.annotations.NotNull;
|
|
|
|
public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
|
|
|
|
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 <E> IRegistry<E> getMinecraftRegistry(ResourceKey<IRegistry<E>> key) {
|
|
return getMinecraftRegistry().registryOrThrow(key);
|
|
}
|
|
|
|
public static <B extends Keyed> Registry<?> createRegistry(Class<B> bukkitClass, IRegistryCustom registryHolder) {
|
|
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 == 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);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private final Class<B> bukkitClass;
|
|
private final Map<NamespacedKey, B> cache = new HashMap<>();
|
|
private final IRegistry<M> minecraftRegistry;
|
|
private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
|
|
private boolean init;
|
|
|
|
public CraftRegistry(Class<B> bukkitClass, IRegistry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> 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 != <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 (!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<B> stream() {
|
|
return minecraftRegistry.keySet().stream().map(minecraftKey -> get(CraftNamespacedKey.fromMinecraft(minecraftKey)));
|
|
}
|
|
|
|
@Override
|
|
public Iterator<B> iterator() {
|
|
return stream().iterator();
|
|
}
|
|
|
|
public B createBukkit(NamespacedKey namespacedKey, M minecraft) {
|
|
if (minecraft == null) {
|
|
return null;
|
|
}
|
|
|
|
return minecraftToBukkit.apply(namespacedKey, minecraft);
|
|
}
|
|
}
|