diff --git a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java index 82f911fde..33c07bc90 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java @@ -1,31 +1,20 @@ package org.bukkit.craftbukkit; -import com.google.common.base.Preconditions; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import org.bukkit.GameEvent; import org.bukkit.NamespacedKey; import org.bukkit.Registry; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.jetbrains.annotations.NotNull; -public class CraftGameEvent extends GameEvent { +public class CraftGameEvent extends GameEvent implements Handleable { public static GameEvent minecraftToBukkit(net.minecraft.world.level.gameevent.GameEvent minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.GAME_EVENT); - GameEvent bukkit = Registry.GAME_EVENT.get(CraftNamespacedKey.fromMinecraft(registry.getKey(minecraft))); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.GAME_EVENT, Registry.GAME_EVENT); } public static net.minecraft.world.level.gameevent.GameEvent bukkitToMinecraft(GameEvent bukkit) { - Preconditions.checkArgument(bukkit != null); - - return ((CraftGameEvent) bukkit).getHandle(); + return CraftRegistry.bukkitToMinecraft(bukkit); } private final NamespacedKey key; @@ -36,6 +25,7 @@ public class CraftGameEvent extends GameEvent { this.handle = handle; } + @Override public net.minecraft.world.level.gameevent.GameEvent getHandle() { return handle; } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java b/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java index 89812da58..69f6539c5 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftMusicInstrument.java @@ -1,32 +1,21 @@ package org.bukkit.craftbukkit; -import com.google.common.base.Preconditions; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.Instrument; import org.bukkit.MusicInstrument; import org.bukkit.NamespacedKey; import org.bukkit.Registry; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.jetbrains.annotations.NotNull; -public class CraftMusicInstrument extends MusicInstrument { +public class CraftMusicInstrument extends MusicInstrument implements Handleable { public static MusicInstrument minecraftToBukkit(Instrument minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.INSTRUMENT); - MusicInstrument bukkit = Registry.INSTRUMENT.get(CraftNamespacedKey.fromMinecraft(registry.getKey(minecraft))); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.INSTRUMENT, Registry.INSTRUMENT); } public static Instrument bukkitToMinecraft(MusicInstrument bukkit) { - Preconditions.checkArgument(bukkit != null); - - return ((CraftMusicInstrument) bukkit).getHandle(); + return CraftRegistry.bukkitToMinecraft(bukkit); } private final NamespacedKey key; @@ -37,6 +26,7 @@ public class CraftMusicInstrument extends MusicInstrument { this.handle = handle; } + @Override public Instrument getHandle() { return handle; } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java index 9ce0d05f1..de46d23aa 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java @@ -23,6 +23,7 @@ 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.enchantments.Enchantment; import org.bukkit.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; @@ -48,6 +49,47 @@ public class CraftRegistry implements Registry { 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); diff --git a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java index 45ef8936b..fef292793 100644 --- a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java +++ b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java @@ -1,7 +1,5 @@ package org.bukkit.craftbukkit.enchantments; -import com.google.common.base.Preconditions; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.enchantment.EnchantmentBinding; @@ -10,29 +8,20 @@ import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.inventory.CraftItemStack; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +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 { +public class CraftEnchantment extends Enchantment implements Handleable { public static Enchantment minecraftToBukkit(net.minecraft.world.item.enchantment.Enchantment minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.ENCHANTMENT); - Enchantment bukkit = Registry.ENCHANTMENT.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.ENCHANTMENT, Registry.ENCHANTMENT); } public static net.minecraft.world.item.enchantment.Enchantment bukkitToMinecraft(Enchantment bukkit) { - Preconditions.checkArgument(bukkit != null); - - return ((CraftEnchantment) bukkit).getHandle(); + return CraftRegistry.bukkitToMinecraft(bukkit); } private final NamespacedKey key; @@ -45,6 +34,7 @@ public class CraftEnchantment extends Enchantment { this.id = BuiltInRegistries.ENCHANTMENT.getId(handle); } + @Override public net.minecraft.world.item.enchantment.Enchantment getHandle() { return handle; } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructure.java b/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructure.java index e0e7f183b..2352b69b3 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructure.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructure.java @@ -1,32 +1,21 @@ package org.bukkit.craftbukkit.generator.structure; -import com.google.common.base.Preconditions; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; -public class CraftStructure extends Structure { +public class CraftStructure extends Structure implements Handleable { public static Structure minecraftToBukkit(net.minecraft.world.level.levelgen.structure.Structure minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.STRUCTURE); - Structure bukkit = Registry.STRUCTURE.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.STRUCTURE, Registry.STRUCTURE); } public static net.minecraft.world.level.levelgen.structure.Structure bukkitToMinecraft(Structure bukkit) { - Preconditions.checkArgument(bukkit != null); - - return ((CraftStructure) bukkit).getHandle(); + return CraftRegistry.bukkitToMinecraft(bukkit); } private final NamespacedKey key; @@ -39,6 +28,7 @@ public class CraftStructure extends Structure { this.structureType = CraftStructureType.minecraftToBukkit(structure.type()); } + @Override public net.minecraft.world.level.levelgen.structure.Structure getHandle() { return structure; } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructureType.java b/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructureType.java index 9d46e3536..af720ad95 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructureType.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/structure/CraftStructureType.java @@ -1,31 +1,20 @@ package org.bukkit.craftbukkit.generator.structure; -import com.google.common.base.Preconditions; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.generator.structure.StructureType; -public class CraftStructureType extends StructureType { +public class CraftStructureType extends StructureType implements Handleable> { public static StructureType minecraftToBukkit(net.minecraft.world.level.levelgen.structure.StructureType minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry> registry = CraftRegistry.getMinecraftRegistry(Registries.STRUCTURE_TYPE); - StructureType bukkit = Registry.STRUCTURE_TYPE.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.STRUCTURE_TYPE, Registry.STRUCTURE_TYPE); } public static net.minecraft.world.level.levelgen.structure.StructureType bukkitToMinecraft(StructureType bukkit) { - Preconditions.checkArgument(bukkit != null); - - return ((CraftStructureType) bukkit).getHandle(); + return CraftRegistry.bukkitToMinecraft(bukkit); } private final NamespacedKey key; @@ -36,6 +25,7 @@ public class CraftStructureType extends StructureType { this.structureType = structureType; } + @Override public net.minecraft.world.level.levelgen.structure.StructureType getHandle() { return structureType; } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java index adce7c037..580dcd16a 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java @@ -1,10 +1,22 @@ package org.bukkit.craftbukkit.inventory.trim; +import net.minecraft.core.registries.Registries; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.craftbukkit.CraftRegistry; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.jetbrains.annotations.NotNull; -public class CraftTrimMaterial implements TrimMaterial { +public class CraftTrimMaterial implements TrimMaterial, Handleable { + + public static TrimMaterial minecraftToBukkit(net.minecraft.world.item.armortrim.TrimMaterial minecraft) { + return CraftRegistry.minecraftToBukkit(minecraft, Registries.TRIM_MATERIAL, Registry.TRIM_MATERIAL); + } + + public static net.minecraft.world.item.armortrim.TrimMaterial bukkitToMinecraft(TrimMaterial bukkit) { + return CraftRegistry.bukkitToMinecraft(bukkit); + } private final NamespacedKey key; private final net.minecraft.world.item.armortrim.TrimMaterial handle; @@ -14,13 +26,14 @@ public class CraftTrimMaterial implements TrimMaterial { this.handle = handle; } + @Override + public net.minecraft.world.item.armortrim.TrimMaterial getHandle() { + return handle; + } + @Override @NotNull public NamespacedKey getKey() { return key; } - - public net.minecraft.world.item.armortrim.TrimMaterial getHandle() { - return handle; - } } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java index 6bb3a6e50..24562431c 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java @@ -1,10 +1,22 @@ package org.bukkit.craftbukkit.inventory.trim; +import net.minecraft.core.registries.Registries; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.craftbukkit.CraftRegistry; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.inventory.meta.trim.TrimPattern; import org.jetbrains.annotations.NotNull; -public class CraftTrimPattern implements TrimPattern { +public class CraftTrimPattern implements TrimPattern, Handleable { + + public static TrimPattern minecraftToBukkit(net.minecraft.world.item.armortrim.TrimPattern minecraft) { + return CraftRegistry.minecraftToBukkit(minecraft, Registries.TRIM_PATTERN, Registry.TRIM_PATTERN); + } + + public static net.minecraft.world.item.armortrim.TrimPattern bukkitToMinecraft(TrimPattern bukkit) { + return CraftRegistry.bukkitToMinecraft(bukkit); + } private final NamespacedKey key; private final net.minecraft.world.item.armortrim.TrimPattern handle; @@ -14,13 +26,14 @@ public class CraftTrimPattern implements TrimPattern { this.handle = handle; } + @Override + public net.minecraft.world.item.armortrim.TrimPattern getHandle() { + return handle; + } + @Override @NotNull public NamespacedKey getKey() { return key; } - - public net.minecraft.world.item.armortrim.TrimPattern getHandle() { - return handle; - } } diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java index dfabbb939..ae87da933 100644 --- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java +++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java @@ -1,19 +1,25 @@ package org.bukkit.craftbukkit.potion; -import com.google.common.base.Preconditions; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.world.effect.MobEffectList; import org.bukkit.Color; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; -public class CraftPotionEffectType extends PotionEffectType { +public class CraftPotionEffectType extends PotionEffectType implements Handleable { + + public static PotionEffectType minecraftToBukkit(MobEffectList minecraft) { + return CraftRegistry.minecraftToBukkit(minecraft, Registries.MOB_EFFECT, Registry.EFFECT); + } + + public static MobEffectList bukkitToMinecraft(PotionEffectType bukkit) { + return CraftRegistry.bukkitToMinecraft(bukkit); + } private final NamespacedKey key; private final MobEffectList handle; @@ -25,6 +31,7 @@ public class CraftPotionEffectType extends PotionEffectType { this.id = CraftRegistry.getMinecraftRegistry(Registries.MOB_EFFECT).getId(handle) + 1; } + @Override public MobEffectList getHandle() { return handle; } @@ -123,21 +130,4 @@ public class CraftPotionEffectType extends PotionEffectType { public String toString() { return "CraftPotionEffectType[" + getKey() + "]"; } - - public static PotionEffectType minecraftToBukkit(MobEffectList minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.MOB_EFFECT); - PotionEffectType bukkit = Registry.EFFECT.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; - } - - public static MobEffectList bukkitToMinecraft(PotionEffectType bukkit) { - Preconditions.checkArgument(bukkit != null); - - return ((CraftPotionEffectType) bukkit).getHandle(); - } } diff --git a/src/main/java/org/bukkit/craftbukkit/util/Handleable.java b/src/main/java/org/bukkit/craftbukkit/util/Handleable.java new file mode 100644 index 000000000..5fe0a9abd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/Handleable.java @@ -0,0 +1,6 @@ +package org.bukkit.craftbukkit.util; + +public interface Handleable { + + M getHandle(); +} diff --git a/src/test/java/org/bukkit/PerRegistryTest.java b/src/test/java/org/bukkit/registry/PerRegistryTest.java similarity index 96% rename from src/test/java/org/bukkit/PerRegistryTest.java rename to src/test/java/org/bukkit/registry/PerRegistryTest.java index 1f2a08568..cf3415b78 100644 --- a/src/test/java/org/bukkit/PerRegistryTest.java +++ b/src/test/java/org/bukkit/registry/PerRegistryTest.java @@ -1,4 +1,4 @@ -package org.bukkit; +package org.bukkit.registry; import static org.junit.jupiter.api.Assertions.*; import com.google.common.collect.Lists; @@ -6,6 +6,9 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Random; import java.util.stream.Stream; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.support.AbstractTestingBase; import org.junit.jupiter.api.BeforeAll; diff --git a/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java b/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java new file mode 100644 index 000000000..4adaafafb --- /dev/null +++ b/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java @@ -0,0 +1,50 @@ +package org.bukkit.registry; + +import static org.junit.jupiter.api.Assertions.*; +import com.google.common.base.Joiner; +import java.util.HashSet; +import java.util.Set; +import org.bukkit.Registry; +import org.bukkit.support.AbstractTestingBase; +import org.bukkit.support.DummyServer; +import org.bukkit.support.provider.RegistriesArgumentProvider; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +/** + * This class tests, if all default registries present in {@link Registry} are added to {@link RegistriesArgumentProvider} + */ +public class RegistryArgumentAddedTest extends AbstractTestingBase { + + @Test + public void testPresent() throws ClassNotFoundException { + // Make sure every registry is created + Class.forName(Registry.class.getName()); + + Set> loadedRegistries = new HashSet<>(DummyServer.registers.keySet()); + Set> notFound = new HashSet<>(); + + RegistriesArgumentProvider + .getData() + .map(Arguments::get) + .map(array -> array[0]) + .map(clazz -> (Class) clazz) + .forEach(clazz -> { + if (!loadedRegistries.remove(clazz)) { + notFound.add(clazz); + } + }); + + assertTrue(loadedRegistries.isEmpty(), String.format(""" + There are registries present, which are not registered in RegistriesArgumentProvider. + + Add the following registries to the RegistriesArgumentProvider class, so that they can be tested. + %s""", Joiner.on('\n').join(loadedRegistries))); + + assertTrue(notFound.isEmpty(), String.format(""" + There are more registries present in RegistriesArgumentProvider then loaded by Registry class. + + Remove the following registries from the RegistriesArgumentProvider class. + %s""", Joiner.on('\n').join(notFound))); + } +} diff --git a/src/test/java/org/bukkit/RegistryConstantsTest.java b/src/test/java/org/bukkit/registry/RegistryConstantsTest.java similarity index 96% rename from src/test/java/org/bukkit/RegistryConstantsTest.java rename to src/test/java/org/bukkit/registry/RegistryConstantsTest.java index a19dca66c..51a825380 100644 --- a/src/test/java/org/bukkit/RegistryConstantsTest.java +++ b/src/test/java/org/bukkit/registry/RegistryConstantsTest.java @@ -1,4 +1,4 @@ -package org.bukkit; +package org.bukkit.registry; import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Field; @@ -9,6 +9,9 @@ import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.resources.MinecraftKey; import net.minecraft.resources.ResourceKey; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; diff --git a/src/test/java/org/bukkit/registry/RegistryConversionTest.java b/src/test/java/org/bukkit/registry/RegistryConversionTest.java new file mode 100644 index 000000000..69dc9bbdf --- /dev/null +++ b/src/test/java/org/bukkit/registry/RegistryConversionTest.java @@ -0,0 +1,306 @@ +package org.bukkit.registry; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; +import static org.mockito.Mockito.*; +import com.google.common.base.Joiner; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import net.minecraft.core.IRegistry; +import net.minecraft.resources.ResourceKey; +import org.bukkit.Bukkit; +import org.bukkit.Keyed; +import org.bukkit.Registry; +import org.bukkit.craftbukkit.util.Handleable; +import org.bukkit.support.AbstractTestingBase; +import org.bukkit.support.provider.RegistryArgumentProvider; +import org.bukkit.support.test.RegistriesTest; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.params.provider.Arguments; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class RegistryConversionTest extends AbstractTestingBase { + + private static final String MINECRAFT_TO_BUKKIT = "minecraftToBukkit"; + private static final String BUKKIT_TO_MINECRAFT = "bukkitToMinecraft"; + + private static final Map, Method> MINECRAFT_TO_BUKKIT_METHODS = new HashMap<>(); + private static final Map, Method> BUKKIT_TO_MINECRAFT_METHODS = new HashMap<>(); + + private static final Set> IMPLEMENT_HANDLE_ABLE = new HashSet<>(); + + @Order(1) + @RegistriesTest + public void testHandleableImplementation(Class clazz) { + Set> notImplemented = new HashSet<>(); + Registry registry = Bukkit.getRegistry(clazz); + + for (Keyed item : registry) { + if (!(item instanceof Handleable)) { + notImplemented.add(item.getClass()); + } + } + + assertTrue(notImplemented.isEmpty(), String.format(""" + Not all implementations of the registry from the class %s have the Handleable interface implemented. + Every Implementation should implement the Handleable interface. + + The following implementation do not implement Handleable: + %s""", clazz.getName(), Joiner.on('\n').join(notImplemented))); + + IMPLEMENT_HANDLE_ABLE.add(clazz); + } + + @Order(2) + @RegistriesTest + public void testMinecraftToBukkitPresent(Class clazz, ResourceKey> registryKey, + Class craftClazz, Class minecraftClazz) { + Method method = null; + try { + method = craftClazz.getDeclaredMethod(MINECRAFT_TO_BUKKIT, minecraftClazz); + } catch (NoSuchMethodException e) { + fail(String.format(""" + The class %s does not have a public static method to convert a minecraft value to a bukkit value. + + Following method should be add which, returns the bukkit value based on the minecraft value. + %s + """, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz))); + } + + assertTrue(Modifier.isPublic(method.getModifiers()), String.format(""" + The method %s in class %s is not public. + + The method should be made public, method structure: + %s + """, MINECRAFT_TO_BUKKIT, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz))); + + assertTrue(Modifier.isStatic(method.getModifiers()), String.format(""" + The method %s in class %s is not static. + + The method should be made static, method structure: + %s + """, MINECRAFT_TO_BUKKIT, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz))); + + assertSame(clazz, method.getReturnType(), String.format(""" + The method %s in class %s has the wrong return value. + + The method should have the correct return value, method structure: + %s + """, MINECRAFT_TO_BUKKIT, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz))); + + MINECRAFT_TO_BUKKIT_METHODS.put(clazz, method); + } + + private String buildMinecraftToBukkitMethod(Class clazz, Class minecraftClazz) { + return String.format(""" + public static %s minecraftToBukkit(%s minecraft) { + [...] + } + """, clazz.getSimpleName(), minecraftClazz.getName()); + } + + @Order(2) + @RegistriesTest + public void testBukkitToMinecraftPresent(Class clazz, ResourceKey> registryKey, + Class craftClazz, Class minecraftClazz) { + Method method = null; + try { + method = craftClazz.getDeclaredMethod(BUKKIT_TO_MINECRAFT, clazz); + } catch (NoSuchMethodException e) { + fail(String.format(""" + The class %s does not have a public static method to convert a bukkit value to a minecraft value. + + Following method should be add which, returns the minecraft value based on the bukkit value. + %s + """, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz))); + } + + assertTrue(Modifier.isPublic(method.getModifiers()), String.format(""" + The method %s in class %s is not public. + + The method should be made public, method structure: + %s + """, BUKKIT_TO_MINECRAFT, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz))); + + assertTrue(Modifier.isStatic(method.getModifiers()), String.format(""" + The method %s in class %s is not static. + + The method should be made static, method structure: + %s + """, BUKKIT_TO_MINECRAFT, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz))); + + assertSame(minecraftClazz, method.getReturnType(), String.format(""" + The method %s in class %s has the wrong return value. + + The method should have the correct return value, method structure: + %s + """, BUKKIT_TO_MINECRAFT, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz))); + + BUKKIT_TO_MINECRAFT_METHODS.put(clazz, method); + } + + private String buildBukkitToMinecraftMethod(Class clazz, Class minecraftClazz) { + return String.format(""" + public static %s bukkitToMinecraft(%s bukkit) { + [...] + } + """, minecraftClazz.getName(), clazz.getSimpleName()); + } + + @Order(2) + @RegistriesTest + public void testMinecraftToBukkitNullValue(Class clazz) throws IllegalAccessException { + checkValidMinecraftToBukkit(clazz); + + try { + Object result = MINECRAFT_TO_BUKKIT_METHODS.get(clazz).invoke(null, (Object) null); + fail(String.format(""" + Method %s in class %s should not accept null values and should throw a IllegalArgumentException. + Got '%s' as return object. + """, MINECRAFT_TO_BUKKIT, clazz.getName(), result)); + } catch (InvocationTargetException e) { + // #invoke wraps the error in a InvocationTargetException, so we need to check it this way + assertSame(IllegalArgumentException.class, e.getCause().getClass(), String.format(""" + Method %s in class %s should not accept null values and should throw a IllegalArgumentException. + """, MINECRAFT_TO_BUKKIT, clazz.getName())); + } + } + + @Order(3) + @RegistriesTest + public void testBukkitToMinecraftNullValue(Class clazz) throws IllegalAccessException { + checkValidBukkitToMinecraft(clazz); + + try { + Object result = BUKKIT_TO_MINECRAFT_METHODS.get(clazz).invoke(null, (Object) null); + fail(String.format(""" + Method %s in class %s should not accept null values and should throw a IllegalArgumentException. + Got '%s' as return object. + """, BUKKIT_TO_MINECRAFT, clazz.getName(), result)); + } catch (InvocationTargetException e) { + // #invoke wraps the error in a InvocationTargetException, so we need to check it this way + assertSame(IllegalArgumentException.class, e.getCause().getClass(), String.format(""" + Method %s in class %s should not accept null values and should throw a IllegalArgumentException. + """, BUKKIT_TO_MINECRAFT, clazz.getName())); + } + } + + @Order(3) + @RegistriesTest + public void testMinecraftToBukkit(Class clazz) { + checkValidMinecraftToBukkit(clazz); + checkValidHandle(clazz); + + Map notMatching = new HashMap<>(); + Method method = MINECRAFT_TO_BUKKIT_METHODS.get(clazz); + + RegistryArgumentProvider.getValues(clazz).map(Arguments::get).forEach(arguments -> { + Keyed bukkit = (Keyed) arguments[0]; + Object minecraft = arguments[1]; + + try { + Object otherBukkit = method.invoke(null, minecraft); + if (bukkit != otherBukkit) { + notMatching.put(bukkit, otherBukkit); + } + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + + assertTrue(notMatching.isEmpty(), String.format(""" + The method %s in class %s does not match all registry items correctly. + + Following registry items where match not correctly: + %s""", MINECRAFT_TO_BUKKIT, clazz.getName(), + Joiner.on('\n').withKeyValueSeparator(" got: ").join(notMatching))); + } + + @Order(3) + @RegistriesTest + public void testBukkitToMinecraft(Class clazz) { + checkValidBukkitToMinecraft(clazz); + checkValidHandle(clazz); + + Map notMatching = new HashMap<>(); + Method method = BUKKIT_TO_MINECRAFT_METHODS.get(clazz); + + RegistryArgumentProvider.getValues(clazz).map(Arguments::get).forEach(arguments -> { + Keyed bukkit = (Keyed) arguments[0]; + Object minecraft = arguments[1]; + + try { + Object otherMinecraft = method.invoke(null, bukkit); + if (minecraft != otherMinecraft) { + notMatching.put(minecraft, otherMinecraft); + } + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + + assertTrue(notMatching.isEmpty(), String.format(""" + The method %s in class %s does not match all registry items correctly. + + Following registry items where match not correctly: + %s""", BUKKIT_TO_MINECRAFT, clazz.getName(), + Joiner.on('\n').withKeyValueSeparator(" got: ").join(notMatching))); + } + + /** + * Minecraft registry can return a default key / value + * when the passed minecraft value is not registry in this case, we want it to throw an error. + */ + @Order(3) + @RegistriesTest + public void testMinecraftToBukkitNoValidMinecraft(Class clazz, ResourceKey> registryKey, + Class craftClazz, Class minecraftClazz) throws IllegalAccessException { + checkValidMinecraftToBukkit(clazz); + + try { + + Object minecraft = mock(minecraftClazz); + Object result = MINECRAFT_TO_BUKKIT_METHODS.get(clazz).invoke(null, minecraft); + fail(String.format(""" + Method %s in class %s should not accept a none registered value and should throw a IllegalStateException. + Got '%s' as return object. + """, MINECRAFT_TO_BUKKIT, clazz.getName(), result)); + } catch (InvocationTargetException e) { + // #invoke wraps the error in a InvocationTargetException, so we need to check it this way + assertSame(IllegalStateException.class, e.getCause().getClass(), String.format(""" + Method %s in class %s should not accept a none registered value and should throw a IllegalStateException. + """, MINECRAFT_TO_BUKKIT, clazz.getName())); + } + } + + private void checkValidBukkitToMinecraft(Class clazz) { + assumeTrue(BUKKIT_TO_MINECRAFT_METHODS.containsKey(clazz), String.format(""" + Cannot test class %s, because it does not have a valid %s method. + + Check test results of testBukkitToMinecraftPresent for more information. + """, clazz.getName(), BUKKIT_TO_MINECRAFT)); + } + + private void checkValidMinecraftToBukkit(Class clazz) { + assumeTrue(MINECRAFT_TO_BUKKIT_METHODS.containsKey(clazz), String.format(""" + Cannot test class %s, because it does not have a valid %s method. + + Check test results of testMinecraftToBukkitPresent for more information. + """, clazz.getName(), MINECRAFT_TO_BUKKIT)); + } + + private void checkValidHandle(Class clazz) { + assumeTrue(IMPLEMENT_HANDLE_ABLE.contains(clazz), String.format(""" + Cannot test class %s, because it does not implement Handleable. + + Check test results of testHandleableImplementation for more information. + """, clazz.getName())); + } +} diff --git a/src/test/java/org/bukkit/RegistryLoadOrderTest.java b/src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java similarity index 98% rename from src/test/java/org/bukkit/RegistryLoadOrderTest.java rename to src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java index 5ae8ad5ce..e3cafb69e 100644 --- a/src/test/java/org/bukkit/RegistryLoadOrderTest.java +++ b/src/test/java/org/bukkit/registry/RegistryLoadOrderTest.java @@ -1,4 +1,4 @@ -package org.bukkit; +package org.bukkit.registry; import static org.junit.jupiter.api.Assertions.*; import com.mojang.serialization.Lifecycle; @@ -9,6 +9,9 @@ import net.minecraft.core.IRegistry; import net.minecraft.core.RegistryMaterials; import net.minecraft.resources.MinecraftKey; import net.minecraft.resources.ResourceKey; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.support.AbstractTestingBase; import org.jetbrains.annotations.NotNull; diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java index d96efc9aa..b19d4f7d1 100644 --- a/src/test/java/org/bukkit/support/DummyServer.java +++ b/src/test/java/org/bukkit/support/DummyServer.java @@ -1,9 +1,13 @@ package org.bukkit.support; import static org.mockito.Mockito.*; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Logger; import org.bukkit.Bukkit; +import org.bukkit.Keyed; import org.bukkit.Material; +import org.bukkit.Registry; import org.bukkit.Server; import org.bukkit.craftbukkit.CraftLootTable; import org.bukkit.craftbukkit.CraftRegistry; @@ -12,9 +16,12 @@ import org.bukkit.craftbukkit.inventory.CraftItemFactory; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.craftbukkit.util.Versioning; +import org.mockito.stubbing.Answer; public final class DummyServer { + public static final Map, Registry> registers = new HashMap<>(); + static { try { Server instance = mock(withSettings().stubOnly()); @@ -36,7 +43,10 @@ public final class DummyServer { when(instance.getLootTable(any())).then(mock -> new CraftLootTable(mock.getArgument(0), AbstractTestingBase.DATA_PACK.getLootData().getLootTable(CraftNamespacedKey.toMinecraft(mock.getArgument(0))))); - when(instance.getRegistry(any())).then(mock -> CraftRegistry.createRegistry(mock.getArgument(0), AbstractTestingBase.REGISTRY_CUSTOM)); + when(instance.getRegistry(any())).then((Answer>) mock -> { + Class aClass = mock.getArgument(0); + return registers.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, AbstractTestingBase.REGISTRY_CUSTOM)); + }); Bukkit.setServer(instance); } catch (Throwable t) { diff --git a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java new file mode 100644 index 000000000..ff86a7a92 --- /dev/null +++ b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java @@ -0,0 +1,53 @@ +package org.bukkit.support.provider; + +import com.google.common.collect.Lists; +import java.util.List; +import java.util.stream.Stream; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.effect.MobEffectList; +import net.minecraft.world.item.Instrument; +import org.bukkit.GameEvent; +import org.bukkit.MusicInstrument; +import org.bukkit.craftbukkit.CraftGameEvent; +import org.bukkit.craftbukkit.CraftMusicInstrument; +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.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.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +public class RegistriesArgumentProvider implements ArgumentsProvider { + + private static final List DATA = Lists.newArrayList(); + + static { + // Order: Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class + DATA.add(Arguments.of(Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class)); + DATA.add(Arguments.of(GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class)); + DATA.add(Arguments.of(MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class)); + DATA.add(Arguments.of(PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffectList.class)); + DATA.add(Arguments.of(Structure.class, Registries.STRUCTURE, CraftStructure.class, net.minecraft.world.level.levelgen.structure.Structure.class)); + DATA.add(Arguments.of(StructureType.class, Registries.STRUCTURE_TYPE, CraftStructureType.class, net.minecraft.world.level.levelgen.structure.StructureType.class)); + DATA.add(Arguments.of(TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.armortrim.TrimMaterial.class)); + DATA.add(Arguments.of(TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.armortrim.TrimPattern.class)); + } + + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + return getData(); + } + + public static Stream getData() { + return DATA.stream(); + } +} diff --git a/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java new file mode 100644 index 000000000..537621806 --- /dev/null +++ b/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java @@ -0,0 +1,33 @@ +package org.bukkit.support.provider; + +import java.util.stream.Stream; +import org.bukkit.Bukkit; +import org.bukkit.Keyed; +import org.bukkit.Registry; +import org.bukkit.craftbukkit.util.Handleable; +import org.bukkit.support.test.RegistryTest; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.support.AnnotationConsumer; + +public class RegistryArgumentProvider implements ArgumentsProvider, AnnotationConsumer { + + private Class registryType; + + @Override + public void accept(RegistryTest registryTest) { + registryType = registryTest.value(); + } + + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + return getValues(registryType); + } + + public static Stream getValues(Class registryType) { + Registry registry = Bukkit.getRegistry(registryType); + return registry.stream().map(keyed -> (Handleable) keyed) + .map(handleAble -> Arguments.of(handleAble, handleAble.getHandle())); + } +} diff --git a/src/test/java/org/bukkit/support/test/RegistriesTest.java b/src/test/java/org/bukkit/support/test/RegistriesTest.java new file mode 100644 index 000000000..c84f38073 --- /dev/null +++ b/src/test/java/org/bukkit/support/test/RegistriesTest.java @@ -0,0 +1,19 @@ +package org.bukkit.support.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.bukkit.support.provider.RegistriesArgumentProvider; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +/** + * Order: Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class + */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(RegistriesArgumentProvider.class) +@ParameterizedTest +public @interface RegistriesTest { +} diff --git a/src/test/java/org/bukkit/support/test/RegistryTest.java b/src/test/java/org/bukkit/support/test/RegistryTest.java new file mode 100644 index 000000000..968c90214 --- /dev/null +++ b/src/test/java/org/bukkit/support/test/RegistryTest.java @@ -0,0 +1,19 @@ +package org.bukkit.support.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.bukkit.Keyed; +import org.bukkit.support.provider.RegistryArgumentProvider; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(RegistryArgumentProvider.class) +@ParameterizedTest +public @interface RegistryTest { + + Class value(); +}