From 2d045fc176072eda7816c960c455ff954b4ab29e Mon Sep 17 00:00:00 2001 From: Jikoo Date: Wed, 22 May 2024 20:00:25 -0400 Subject: [PATCH] Update to 1.20.6 No longer supports 1.20.4 and lower due to changes to constants Still need to add methods to get tags for enchanting table/anvil applicable materials from enchantments. --- pom.xml | 8 +- .../planarenchanting/anvil/AnvilFunction.java | 21 +- .../anvil/CombineEnchantments.java | 15 +- .../anvil/RepairMaterial.java | 8 +- .../planarenchanting/enchant/EnchantData.java | 223 ++++++++++-------- .../enchant/EnchantDataReflection.java | 33 +-- .../enchant/EnchantRarity.java | 46 ---- .../table/Enchantability.java | 2 + .../table/TableEnchantListener.java | 21 +- .../jikoo/planarenchanting/util/ItemUtil.java | 23 ++ .../anvil/AnvilFunctionTest.java | 18 +- .../anvil/AnvilOperationStateTest.java | 18 +- .../anvil/AnvilOperationTest.java | 24 +- .../anvil/CombineEnchantmentsTest.java | 40 +++- .../anvil/RepairMaterialTest.java | 25 +- .../enchant/EnchantDataTest.java | 9 +- .../enchant/EnchantmentReflectionTest.java | 47 ++-- .../enchant/EnchantmentUtilTest.java | 18 +- .../table/EnchantabilityTest.java | 3 +- .../table/EnchantingTableTest.java | 10 +- .../table/EnchantingTableUtilTest.java | 7 +- .../table/TableEnchantListenerTest.java | 12 +- .../util/mock/ServerMocks.java | 51 ++++ .../planarenchanting/util/mock/TagMocks.java | 41 ---- .../mock/enchantments/EnchantmentMocks.java | 140 +++++------ .../world/item/enchantment/Enchantment.java | 12 +- 26 files changed, 424 insertions(+), 451 deletions(-) delete mode 100644 src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantRarity.java delete mode 100644 src/test/java/com/github/jikoo/planarenchanting/util/mock/TagMocks.java diff --git a/pom.xml b/pom.xml index 680a146..1c1994b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.jikoo planarenchanting - 1.1.1-SNAPSHOT + 2.0.0-SNAPSHOT PlanarEnchanting @@ -49,10 +49,6 @@ jitpack.io https://jitpack.io - - papermc - https://repo.papermc.io/repository/maven-public/ - @@ -66,7 +62,7 @@ org.spigotmc spigot-api - 1.20.4-R0.1-SNAPSHOT + 1.20.6-R0.1-SNAPSHOT provided diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java index 4cb513a..93cf5b3 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java @@ -1,18 +1,16 @@ package com.github.jikoo.planarenchanting.anvil; import com.github.jikoo.planarenchanting.enchant.EnchantData; -import com.github.jikoo.planarenchanting.enchant.EnchantRarity; import com.github.jikoo.planarenchanting.util.ItemUtil; import com.github.jikoo.planarenchanting.util.MetaCachedStack; +import java.util.Objects; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; -import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.Repairable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Objects; /** * An interface representing a portion of the functionality of an anvil. By using several in @@ -240,19 +238,16 @@ protected int getNonApplicableCost() { /** Constant for combining enchantments from source and target like Bedrock Edition. */ AnvilFunction COMBINE_ENCHANTMENTS_BEDROCK_EDITION = new CombineEnchantments() { @Override - protected EnchantRarity getRarity(Enchantment enchantment) { - // Bedrock Edition rarity is 1 tier lower for trident enchantments. - if (enchantment.getItemTarget() != EnchantmentTarget.TRIDENT) { - return super.getRarity(enchantment); + protected int getAnvilCost(Enchantment enchantment, boolean isFromBook) { + EnchantData enchantData = EnchantData.of(enchantment); + if (!enchantData.getSecondaryItems().isTagged(Material.TRIDENT)) { + return super.getAnvilCost(enchantment, isFromBook); } - EnchantRarity rarity = EnchantData.of(enchantment).getRarity(); + int base = enchantData.getAnvilCost(); - if (rarity.ordinal() < 1) { - return rarity; - } - - return EnchantRarity.values()[rarity.ordinal() - 1]; + // Bedrock Edition rarity is 1 tier lower for trident enchantments. + return Math.max(1, isFromBook ? base / 4 : base / 2); } @Override diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java index 3b1d6aa..3d74443 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java @@ -1,16 +1,15 @@ package com.github.jikoo.planarenchanting.anvil; import com.github.jikoo.planarenchanting.enchant.EnchantData; -import com.github.jikoo.planarenchanting.enchant.EnchantRarity; import com.github.jikoo.planarenchanting.enchant.EnchantmentUtil; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; abstract class CombineEnchantments implements AnvilFunction { @@ -77,12 +76,8 @@ public void modifyResult(@Nullable ItemMeta itemMeta) { } - protected EnchantRarity getRarity(Enchantment enchantment) { - return EnchantData.of(enchantment).getRarity(); - } - - private int getAnvilCost(Enchantment enchantment, boolean isFromBook) { - int value = getRarity(enchantment).getAnvilValue(); + protected int getAnvilCost(Enchantment enchantment, boolean isFromBook) { + int value = EnchantData.of(enchantment).getAnvilCost(); return isFromBook ? Math.max(1, value / 2) : value; } diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/RepairMaterial.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/RepairMaterial.java index d2de12a..643a566 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/RepairMaterial.java +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/RepairMaterial.java @@ -1,6 +1,6 @@ package com.github.jikoo.planarenchanting.anvil; -import java.util.EnumMap; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.bukkit.Material; @@ -30,8 +30,7 @@ public static boolean repairs(@NotNull ItemStack base, @NotNull ItemStack additi return recipeChoice != null && recipeChoice.test(addition); } - private static final Map MATERIALS_TO_REPAIRABLE - = new EnumMap<>(Material.class); + private static final Map MATERIALS_TO_REPAIRABLE = new HashMap<>(); static { String[] armor = new String[] { "_HELMET", "_CHESTPLATE", "_LEGGINGS", "_BOOTS" }; @@ -62,8 +61,9 @@ public static boolean repairs(@NotNull ItemStack base, @NotNull ItemStack additi addGear("NETHERITE", armortools, Material.NETHERITE_INGOT); // Misc. repairable items - MATERIALS_TO_REPAIRABLE.put(Material.TURTLE_HELMET, singleChoice(Material.SCUTE)); + MATERIALS_TO_REPAIRABLE.put(Material.TURTLE_HELMET, singleChoice(Material.TURTLE_SCUTE)); MATERIALS_TO_REPAIRABLE.put(Material.ELYTRA, singleChoice(Material.PHANTOM_MEMBRANE)); + MATERIALS_TO_REPAIRABLE.put(Material.MACE, singleChoice(Material.BREEZE_ROD)); } private static void addGear(String type, String[] gearType, RecipeChoice repairChoice) { diff --git a/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantData.java b/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantData.java index 5bc442a..f9e6957 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantData.java +++ b/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantData.java @@ -1,120 +1,106 @@ package com.github.jikoo.planarenchanting.enchant; +import com.github.jikoo.planarenchanting.util.ItemUtil; import com.github.jikoo.planarwrappers.util.WeightedRandom; -import org.bukkit.enchantments.Enchantment; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.TestOnly; import java.util.HashMap; import java.util.Map; import java.util.function.IntUnaryOperator; +import org.bukkit.Material; +import org.bukkit.Tag; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; public class EnchantData implements WeightedRandom.Choice { private static final Map ENCHANT_DATA = new HashMap<>(); static { - IntUnaryOperator modLootBonus = modLvl(15, 9); - IntUnaryOperator modLootMax = level -> modLootBonus.applyAsInt(level) + 50; - addLoot(Enchantment.LOOT_BONUS_MOBS, modLootBonus, modLootMax); - addLoot(Enchantment.LOOT_BONUS_BLOCKS, modLootBonus, modLootMax); - addLoot(Enchantment.LUCK, modLootBonus, modLootMax); - addLoot(Enchantment.LURE, modLootBonus, modLootMax); - - addProtection(Enchantment.PROTECTION_ENVIRONMENTAL, EnchantRarity.COMMON, 1, 11); - addProtection(Enchantment.PROTECTION_FIRE, EnchantRarity.UNCOMMON, 10, 8); - addProtection(Enchantment.PROTECTION_FALL, EnchantRarity.UNCOMMON, 5, 6); - addProtection(Enchantment.PROTECTION_EXPLOSIONS, EnchantRarity.RARE, 5, 8); - addProtection(Enchantment.PROTECTION_PROJECTILE, EnchantRarity.UNCOMMON, 3, 6); - - IntUnaryOperator lvlTimes10 = level -> level * 10; - - add(Enchantment.OXYGEN, EnchantRarity.RARE, lvlTimes10, 30); - add(Enchantment.WATER_WORKER, EnchantRarity.RARE, flat(1), 40); - add(Enchantment.THORNS, EnchantRarity.VERY_RARE, modLvl(10, 20)); - add(Enchantment.DEPTH_STRIDER, EnchantRarity.RARE, lvlTimes10, 15); - add(Enchantment.FROST_WALKER, EnchantRarity.RARE, lvlTimes10, 15); - add(Enchantment.SOUL_SPEED, EnchantRarity.VERY_RARE, lvlTimes10, 15); - IntUnaryOperator lvlTimes25 = level -> level * 25; - add(Enchantment.SWIFT_SNEAK, EnchantRarity.VERY_RARE, lvlTimes25, 50); - - add(Enchantment.DAMAGE_ALL, EnchantRarity.COMMON, modLvl(1, 11), 20); - add(Enchantment.DAMAGE_UNDEAD, EnchantRarity.UNCOMMON, modLvl(5, 8), 20); - add(Enchantment.DAMAGE_ARTHROPODS, EnchantRarity.UNCOMMON, modLvl(5, 8), 20); - add(Enchantment.KNOCKBACK, EnchantRarity.UNCOMMON, modLvl(5, 20)); - add(Enchantment.FIRE_ASPECT, EnchantRarity.RARE, modLvl(10, 20)); - add(Enchantment.SWEEPING_EDGE, EnchantRarity.RARE, modLvl(5, 9), 15); - - add(Enchantment.DIG_SPEED, EnchantRarity.COMMON, modLvl(1, 10)); - add(Enchantment.SILK_TOUCH, EnchantRarity.VERY_RARE, flat(15), modLvl(61, 10)); - add(Enchantment.DURABILITY, EnchantRarity.UNCOMMON, modLvl(5, 8)); - - IntUnaryOperator flat25 = flat(25); - IntUnaryOperator flat50 = flat(50); - - add(Enchantment.VANISHING_CURSE, EnchantRarity.VERY_RARE, flat25, flat50); - add(Enchantment.BINDING_CURSE, EnchantRarity.VERY_RARE, flat25, flat50); - - IntUnaryOperator flat20 = flat(20); - - add(Enchantment.ARROW_DAMAGE, EnchantRarity.COMMON, modLvl(1, 10), 15); - add(Enchantment.ARROW_KNOCKBACK, EnchantRarity.RARE, modLvl(12, 20), 25); - add(Enchantment.ARROW_FIRE, EnchantRarity.RARE, flat20, flat50); - add(Enchantment.ARROW_INFINITE, EnchantRarity.VERY_RARE, flat20, flat50); - add(Enchantment.LOYALTY, EnchantRarity.UNCOMMON, modLvl(12, 7), flat50); - add(Enchantment.IMPALING, EnchantRarity.RARE, modLvl(1, 8), 20); - add(Enchantment.RIPTIDE, EnchantRarity.RARE, modLvl(17, 7), flat50); - add(Enchantment.CHANNELING, EnchantRarity.VERY_RARE, flat25, flat50); - add(Enchantment.MULTISHOT, EnchantRarity.RARE, flat20, flat50); - add(Enchantment.QUICK_CHARGE, EnchantRarity.UNCOMMON, level -> 12 + (level - 1) * 20, flat50); - add(Enchantment.PIERCING, EnchantRarity.COMMON, modLvl(1, 10), flat50); - add(Enchantment.MENDING, EnchantRarity.RARE, lvlTimes25); - } - - private static @NotNull IntUnaryOperator modLvl(int base, int levelMod) { - return level -> base + (level - 1) * levelMod; - } - - private static @NotNull IntUnaryOperator flat(int value) { - return integer -> value; - } - - private static void add( - @NotNull Enchantment enchantment, - @NotNull EnchantRarity enchantRarity, - @NotNull IntUnaryOperator min, - @NotNull IntUnaryOperator max) { - EnchantData data = new EnchantData(enchantment, enchantRarity, min, max); - ENCHANT_DATA.put(data.getEnchantment(), data); + // NMSREF net.minecraft.world.item.enchantment.Enchantments + // Armor + add(Enchantment.PROTECTION, Tag.ITEMS_ENCHANTABLE_ARMOR, 10, 1, perLvl(1, 11), perLvl(12, 11)); + add(Enchantment.FIRE_PROTECTION, Tag.ITEMS_ENCHANTABLE_ARMOR, 5, 2, perLvl(10, 8), perLvl(18, 8)); + add(Enchantment.FEATHER_FALLING, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, 5, 2, perLvl(5, 6), perLvl(11, 6)); + add(Enchantment.BLAST_PROTECTION, Tag.ITEMS_ENCHANTABLE_ARMOR, 2, 4, perLvl(1, 11), perLvl(12, 11)); + add(Enchantment.PROJECTILE_PROTECTION, Tag.ITEMS_ENCHANTABLE_ARMOR, 5, 2, perLvl(3, 6), perLvl(9, 6)); + add(Enchantment.RESPIRATION, Tag.ITEMS_ENCHANTABLE_HEAD_ARMOR, 2, 4, perLvl(10, 10), perLvl(40, 10)); + add(Enchantment.AQUA_AFFINITY, Tag.ITEMS_ENCHANTABLE_HEAD_ARMOR, 2, 4, flat(1), flat(41)); + add(Enchantment.THORNS, Tag.ITEMS_ENCHANTABLE_CHEST_ARMOR, 1, 8, perLvl(10, 20), perLvl(60, 20)); + add(Enchantment.DEPTH_STRIDER, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, 2, 4, perLvl(10, 10), perLvl(25, 10)); + add(Enchantment.FROST_WALKER, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, 2, 4, perLvl(10, 10), perLvl(25, 10)); + add(Enchantment.BINDING_CURSE, Tag.ITEMS_ENCHANTABLE_EQUIPPABLE, 1, 8, flat(25), flat(50)); + add(Enchantment.SOUL_SPEED, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, 1, 8, perLvl(10, 10), perLvl(25, 10)); + add(Enchantment.SWIFT_SNEAK, Tag.ITEMS_ENCHANTABLE_LEG_ARMOR, 1, 8, perLvl(25, 25), perLvl(75, 25)); + // Melee weapon + add(Enchantment.SHARPNESS, Tag.ITEMS_ENCHANTABLE_SHARP_WEAPON, Tag.ITEMS_ENCHANTABLE_SWORD, 10, 1, perLvl(1, 11), perLvl(21, 11)); + add(Enchantment.SMITE, Tag.ITEMS_ENCHANTABLE_WEAPON, Tag.ITEMS_ENCHANTABLE_SWORD, 5, 2, perLvl(5, 8), perLvl(25, 8)); + add(Enchantment.BANE_OF_ARTHROPODS, Tag.ITEMS_ENCHANTABLE_WEAPON, Tag.ITEMS_ENCHANTABLE_SWORD, 5, 2, perLvl(5, 8), perLvl(25, 8)); + add(Enchantment.KNOCKBACK, Tag.ITEMS_ENCHANTABLE_SWORD, 5, 2, perLvl(5, 20), perLvl(55, 20)); + add(Enchantment.FIRE_ASPECT, Tag.ITEMS_ENCHANTABLE_FIRE_ASPECT, 2, 4, perLvl(10, 20), perLvl(60, 20)); + add(Enchantment.LOOTING, Tag.ITEMS_ENCHANTABLE_SWORD, 2, 4, perLvl(15, 9), perLvl(65, 9)); + add(Enchantment.SWEEPING_EDGE, Tag.ITEMS_ENCHANTABLE_SWORD, 2, 4, perLvl(5, 9), perLvl(20, 9)); + // Tool + add(Enchantment.EFFICIENCY, Tag.ITEMS_ENCHANTABLE_MINING, 10, 1, perLvl(1, 10), perLvl(51, 10)); + add(Enchantment.SILK_TOUCH, Tag.ITEMS_ENCHANTABLE_MINING_LOOT, 1, 8, flat(15), flat(65)); + add(Enchantment.UNBREAKING, Tag.ITEMS_ENCHANTABLE_DURABILITY, 5, 2, perLvl(5, 8), perLvl(55, 8)); + add(Enchantment.FORTUNE, Tag.ITEMS_ENCHANTABLE_MINING_LOOT, 2, 4, perLvl(15, 9), perLvl(65, 9)); + // Bow + add(Enchantment.POWER, Tag.ITEMS_ENCHANTABLE_BOW, 10, 1, perLvl(1, 10), perLvl(16, 10)); + add(Enchantment.PUNCH, Tag.ITEMS_ENCHANTABLE_BOW, 2, 4, perLvl(12, 20), perLvl(37, 20)); + add(Enchantment.FLAME, Tag.ITEMS_ENCHANTABLE_BOW, 2, 4, flat(20), flat(50)); + add(Enchantment.INFINITY, Tag.ITEMS_ENCHANTABLE_BOW, 1, 8, flat(20), flat(50)); + // Fishing rod + add(Enchantment.LUCK_OF_THE_SEA, Tag.ITEMS_ENCHANTABLE_FISHING, 2, 4, perLvl(15, 9), perLvl(65, 9)); + add(Enchantment.LURE, Tag.ITEMS_ENCHANTABLE_FISHING, 2, 4, perLvl(15, 9), perLvl(65, 9)); + // Trident + add(Enchantment.LOYALTY, Tag.ITEMS_ENCHANTABLE_TRIDENT, 5, 2, perLvl(12, 7), flat(50)); + add(Enchantment.IMPALING, Tag.ITEMS_ENCHANTABLE_TRIDENT, 2, 4, perLvl(1, 8), perLvl(21, 8)); + add(Enchantment.RIPTIDE, Tag.ITEMS_ENCHANTABLE_TRIDENT, 2, 4, perLvl(17, 7), flat(50)); + add(Enchantment.CHANNELING, Tag.ITEMS_ENCHANTABLE_TRIDENT, 1, 8, flat(25), flat(50)); + // Crossbow + add(Enchantment.MULTISHOT, Tag.ITEMS_ENCHANTABLE_CROSSBOW, 2, 4, flat(20), flat(50)); + add(Enchantment.QUICK_CHARGE, Tag.ITEMS_ENCHANTABLE_CROSSBOW, 5, 2, perLvl(12, 20), flat(50)); + add(Enchantment.PIERCING, Tag.ITEMS_ENCHANTABLE_CROSSBOW, 10, 1, perLvl(1, 10), flat(50)); + // General + add(Enchantment.MENDING, Tag.ITEMS_ENCHANTABLE_DURABILITY, 2, 4, perLvl(25, 25), perLvl(75, 25)); + add(Enchantment.VANISHING_CURSE, Tag.ITEMS_ENCHANTABLE_VANISHING, 1, 8, flat(25), flat(50)); + // Mace + // NMSREF net.minecraft.world.item.enchantment.BreachEnchantment + add(Enchantment.BREACH, Tag.ITEMS_ENCHANTABLE_MACE, 2, 4, perLvl(15, 9), perLvl(65, 9)); + // NMSREF net.minecraft.world.item.enchantment.DensityEnchantment + add(Enchantment.DENSITY, Tag.ITEMS_ENCHANTABLE_MACE, 10, 1, perLvl(1, 11), perLvl(21, 11)); + // NMSREF net.minecraft.world.item.enchantment.WindBurstEnchantment + add(Enchantment.WIND_BURST, Tag.ITEMS_ENCHANTABLE_MACE, 2, 4, perLvl(15, 9), perLvl(65, 9)); } private static void add( - @NotNull Enchantment enchantment, - @NotNull EnchantRarity enchantRarity, - @NotNull IntUnaryOperator min, - int maxMod) { - add(enchantment, enchantRarity, min, level -> min.applyAsInt(level) + maxMod); + @NotNull Enchantment enchant, + @Nullable Tag primaryItems, + @NotNull Tag secondaryItems, + int weight, + int anvilCost, + @NotNull IntUnaryOperator minEnchantQuality, + @NotNull IntUnaryOperator maxEnchantQuality) { + ENCHANT_DATA.put(enchant, new EnchantData(enchant, primaryItems, secondaryItems, weight, anvilCost, minEnchantQuality, maxEnchantQuality)); } private static void add( - @NotNull Enchantment enchantment, - @NotNull EnchantRarity enchantRarity, - @NotNull IntUnaryOperator min) { - add(enchantment, enchantRarity, min, 50); + @NotNull Enchantment enchant, + @NotNull Tag items, + int weight, + int anvilCost, + @NotNull IntUnaryOperator minEnchantQuality, + @NotNull IntUnaryOperator maxEnchantQuality) { + ENCHANT_DATA.put(enchant, new EnchantData(enchant, enchant.isTreasure() ? ItemUtil.TAG_EMPTY : null, items, weight, anvilCost, minEnchantQuality, maxEnchantQuality)); } - private static void addProtection( - @NotNull Enchantment enchantment, - @NotNull EnchantRarity enchantRarity, - int base, - int levelMod) { - add(enchantment, enchantRarity, modLvl(base, levelMod), levelMod); + private static @NotNull IntUnaryOperator perLvl(int base, int perLevel) { + return level -> base + (level - 1) * perLevel; } - private static void addLoot( - @NotNull Enchantment enchantment, - @NotNull IntUnaryOperator min, - @NotNull IntUnaryOperator max) { - add(enchantment, EnchantRarity.RARE, min, max); + private static @NotNull IntUnaryOperator flat(int value) { + return integer -> value; } @TestOnly @@ -127,38 +113,69 @@ public static EnchantData of(@NotNull Enchantment enchantment) { } private final @NotNull Enchantment enchantment; - private final @NotNull EnchantRarity enchantRarity; + private final @Nullable Tag primaryItems; + private final @NotNull Tag secondaryItems; + private final int weight; + private final int anvilCost; private final @NotNull IntUnaryOperator minCost; private final @NotNull IntUnaryOperator maxCost; private EnchantData(@NotNull Enchantment enchantment) { - this(enchantment, EnchantDataReflection.getRarity(enchantment), + this(enchantment, + null, // TODO + Tag.WALL_SIGNS, // TODO + EnchantDataReflection.getWeight(enchantment), + EnchantDataReflection.getAnvilCost(enchantment), EnchantDataReflection.getMinCost(enchantment), EnchantDataReflection.getMaxCost(enchantment)); } private EnchantData( @NotNull Enchantment enchantment, - @NotNull EnchantRarity enchantRarity, + @Nullable Tag primaryItems, + @NotNull Tag secondaryItems, + int weight, + int anvilCost, @NotNull IntUnaryOperator minEnchantQuality, @NotNull IntUnaryOperator maxEnchantQuality) { this.enchantment = enchantment; - this.enchantRarity = enchantRarity; + this.primaryItems = primaryItems; + this.secondaryItems = secondaryItems; + this.weight = weight; + this.anvilCost = anvilCost; this.minCost = minEnchantQuality; this.maxCost = maxEnchantQuality; } public @NotNull Enchantment getEnchantment() { - return this.enchantment; + return enchantment; } - public @NotNull EnchantRarity getRarity() { - return this.enchantRarity; + /** + * Get a {@link Tag} containing the items this enchantment is intended to apply to from an enchanting table. + * + *

In vanilla, this is used specifically for enchantments that can be applied to different + * items by a table and anvil, i.e. sharpness. Enchantments with identical table and anvil lists + * report their secondary list as being the primary. For treasure enchantments, the primary tag is + * empty if not available (unlike vanilla). + * + * @return + */ + public @NotNull Tag getPrimaryItems() { + return primaryItems == null ? secondaryItems : primaryItems; + } + + public @NotNull Tag getSecondaryItems() { + return secondaryItems; } @Override public int getWeight() { - return this.getRarity().getWeight(); + return this.weight; + } + + public int getAnvilCost() { + return this.anvilCost; } public int getMinCost(int level) { diff --git a/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantDataReflection.java b/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantDataReflection.java index af8b50e..2a9f4f1 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantDataReflection.java +++ b/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantDataReflection.java @@ -14,20 +14,25 @@ public final class EnchantDataReflection { /** - * Fetch an {@link Enchantment Enchantment's} internal rarity. + * Fetch an {@link Enchantment Enchantment's} internal weight. * * @param enchantment the {@code Enchantment} - * @return the rarity or UNKNOWN if unable to fetch + * @return the weight or 0 if unknown */ - public static EnchantRarity getRarity(Enchantment enchantment) { - return nmsHandler(enchantment, nmsEnchant -> { - // NMSREF net.minecraft.world.item.enchantment.Enchantment#getRarity() - Object enchantmentRarity = nmsEnchant.getClass().getDeclaredMethod("d").invoke(nmsEnchant); - // NMSREF net.minecraft.world.item.enchantment.Enchantment$EnchantRarity#getWeight() - int weight = (int) enchantmentRarity.getClass().getDeclaredMethod("a") - .invoke(enchantmentRarity); - return EnchantRarity.of(weight); - }, EnchantRarity.UNKNOWN); + public static int getWeight(@NotNull Enchantment enchantment) { + // NMSREF net.minecraft.world.item.enchantment.Enchantment#getWeight + return nmsHandler(enchantment, nmsEnchant -> (int) nmsEnchant.getClass().getMethod("d").invoke(nmsEnchant), 0); + } + + /** + * Fetch an {@link Enchantment Enchantment's} internal anvil cost multiplier. + * + * @param enchantment the {@code Enchantment} + * @return the anvil cost multiplier or 40 if unknown + */ + public static int getAnvilCost(@NotNull Enchantment enchantment) { + // NMSREF net.minecraft.world.item.enchantment.Enchantment#getAnvilCost + return nmsHandler(enchantment, nmsEnchant -> (int) nmsEnchant.getClass().getMethod("e").invoke(nmsEnchant), 40); } /** @@ -38,7 +43,7 @@ public static EnchantRarity getRarity(Enchantment enchantment) { */ public static IntUnaryOperator getMinCost(Enchantment enchantment) { // NMSREF net.minecraft.world.item.enchantment.Enchantment#getMinCost(int) - return nmsIntUnaryOperator(enchantment, "a", EnchantDataReflection::defaultMinEnchantQuality); + return nmsIntUnaryOperator(enchantment, "c", EnchantDataReflection::defaultMinEnchantQuality); } /** @@ -59,7 +64,7 @@ private static int defaultMinEnchantQuality(int level) { */ public static IntUnaryOperator getMaxCost(Enchantment enchantment) { // NMSREF net.minecraft.world.item.enchantment.Enchantment#getMaxCost(int) - return nmsIntUnaryOperator(enchantment, "b", EnchantDataReflection::defaultMaxEnchantQuality); + return nmsIntUnaryOperator(enchantment, "d", EnchantDataReflection::defaultMaxEnchantQuality); } @@ -84,7 +89,7 @@ private static int defaultMaxEnchantQuality(int level) { private static IntUnaryOperator nmsIntUnaryOperator(@NotNull Enchantment enchantment, @NotNull String methodName, @NotNull IntUnaryOperator defaultOperator) { return nmsHandler(enchantment, nmsEnchant -> { - Method method = nmsEnchant.getClass().getDeclaredMethod(methodName, int.class); + Method method = nmsEnchant.getClass().getMethod(methodName, int.class); return level -> { try { return (int) method.invoke(nmsEnchant, level); diff --git a/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantRarity.java b/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantRarity.java deleted file mode 100644 index 2fc610e..0000000 --- a/src/main/java/com/github/jikoo/planarenchanting/enchant/EnchantRarity.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.jikoo.planarenchanting.enchant; - -/** - * A representation of enchantment rarity. - */ -public enum EnchantRarity { - - // Note: must be ordered from least to most rare! - COMMON(10, 1), - UNCOMMON(5, 2), - RARE(2, 4), - VERY_RARE(1, 8), - UNKNOWN(0, 40); - - private final int weight; - private final int anvilMultiplier; - - EnchantRarity(int weight, int anvilMultiplier) { - this.weight = weight; - this.anvilMultiplier = anvilMultiplier; - } - - public int getWeight() { - return weight; - } - - public int getAnvilValue() { - return anvilMultiplier; - } - - /** - * Get a rarity based on weight. - * - * @param weight the weight of the rarity - * @return the rarity or {@link #UNKNOWN} if none match - */ - static EnchantRarity of(int weight) { - for (EnchantRarity value : values()) { - if (value.weight == weight) { - return value; - } - } - return UNKNOWN; - } - -} diff --git a/src/main/java/com/github/jikoo/planarenchanting/table/Enchantability.java b/src/main/java/com/github/jikoo/planarenchanting/table/Enchantability.java index cd13299..37818b5 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/table/Enchantability.java +++ b/src/main/java/com/github/jikoo/planarenchanting/table/Enchantability.java @@ -33,6 +33,7 @@ public record Enchantability(@Range(from = 1, to = Integer.MAX_VALUE) int value) public static final Enchantability GOLD_TOOL; public static final Enchantability BOOK; public static final Enchantability TRIDENT; + public static final Enchantability MACE; /** * Get the {@code Enchantability} of a {@link Material}. Will return {@code null} if not @@ -78,6 +79,7 @@ public record Enchantability(@Range(from = 1, to = Integer.MAX_VALUE) int value) BOOK = addMaterial(Material.BOOK, new Enchantability(1)); BY_MATERIAL.put(Material.ENCHANTED_BOOK, BOOK); TRIDENT = addMaterial(Material.TRIDENT, BOOK); + MACE = addMaterial(Material.MACE, LEATHER); } @Contract("_, _ -> param2") diff --git a/src/main/java/com/github/jikoo/planarenchanting/table/TableEnchantListener.java b/src/main/java/com/github/jikoo/planarenchanting/table/TableEnchantListener.java index f696d2d..b79c63c 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/table/TableEnchantListener.java +++ b/src/main/java/com/github/jikoo/planarenchanting/table/TableEnchantListener.java @@ -10,7 +10,6 @@ import org.bukkit.event.enchantment.EnchantItemEvent; import org.bukkit.event.enchantment.PrepareItemEnchantEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -142,25 +141,7 @@ private void randomizeSeed(@NotNull Player player, @NotNull IntSupplier supplier * @return the enchantment seed */ private long getSeed(@NotNull Player player, int buttonIndex) { - return getEnchantmentSeed(player) + buttonIndex; - } - - /** - * Obtain the enchantment seed from the {@link Player}. If not present, generates a new seed. - * - * @param player the {@link Player} - * @return the enchantment seed - */ - private long getEnchantmentSeed(@NotNull Player player) { - // Use legacy existing seed if available. - var seed = player.getPersistentDataContainer().get(key, PersistentDataType.LONG); - - if (seed == null) { - // If legacy seed is not available, use internal seed. - return player.getEnchantmentSeed(); - } - - return seed; + return player.getEnchantmentSeed() + buttonIndex; } /** diff --git a/src/main/java/com/github/jikoo/planarenchanting/util/ItemUtil.java b/src/main/java/com/github/jikoo/planarenchanting/util/ItemUtil.java index 4e44a28..1363de4 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/util/ItemUtil.java +++ b/src/main/java/com/github/jikoo/planarenchanting/util/ItemUtil.java @@ -1,6 +1,10 @@ package com.github.jikoo.planarenchanting.util; +import java.util.Objects; +import java.util.Set; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.Repairable; @@ -20,6 +24,25 @@ public void setType(@NotNull Material type) { throw new UnsupportedOperationException("Cannot modify AIR constant."); } }; + /** Constant representing an empty item {@link Tag}. */ + public static final Tag TAG_EMPTY = new Tag<>() { + @Override + public boolean isTagged(@NotNull Material item) { + return false; + } + + @NotNull + @Override + public Set getValues() { + return Set.of(); + } + + @NotNull + @Override + public NamespacedKey getKey() { + return Objects.requireNonNull(NamespacedKey.fromString("planarenchanting:empty")); + } + }; /** * Check if an {@link ItemStack} is empty. diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java index dc755dd..f288236 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java @@ -10,6 +10,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import com.github.jikoo.planarenchanting.anvil.mock.ReadableResultState; @@ -17,17 +18,15 @@ import com.github.jikoo.planarenchanting.util.mock.enchantments.EnchantmentMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.InventoryMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.ItemFactoryMocks; -import com.github.jikoo.planarenchanting.util.mock.TagMocks; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Stream; -import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.NamespacedKey; import org.bukkit.Server; +import org.bukkit.Tag; import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; @@ -63,12 +62,13 @@ void beforeAll() { when(server.getItemFactory()).thenReturn(factory); // RepairMaterial requires these tags to be set up to test. - TagMocks.mockTag(server, "items", NamespacedKey.minecraft("stone_tool_materials"), Material.class, - List.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)); - TagMocks.mockTag(server, "blocks", NamespacedKey.minecraft("planks"), Material.class, - List.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)); //etc. non-exhaustive list + Tag tag = Tag.ITEMS_STONE_TOOL_MATERIALS; + doReturn(Set.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)) + .when(tag).getValues(); + tag = Tag.PLANKS; + doReturn(Set.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)) //etc. non-exhaustive list + .when(tag).getValues(); - Bukkit.setServer(server); EnchantmentMocks.init(server); } diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java index b074b37..b4cb36c 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java @@ -5,20 +5,19 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import com.github.jikoo.planarenchanting.anvil.mock.ReadableResultState; import com.github.jikoo.planarenchanting.util.MetaCachedStack; import com.github.jikoo.planarenchanting.util.mock.ServerMocks; -import com.github.jikoo.planarenchanting.util.mock.TagMocks; import com.github.jikoo.planarenchanting.util.mock.enchantments.EnchantmentMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.InventoryMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.ItemFactoryMocks; -import java.util.List; -import org.bukkit.Bukkit; +import java.util.Set; import org.bukkit.Material; -import org.bukkit.NamespacedKey; import org.bukkit.Server; +import org.bukkit.Tag; import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; @@ -47,12 +46,13 @@ void beforeAll() { when(server.getItemFactory()).thenReturn(factory); // RepairMaterial requires these tags to be set up. - TagMocks.mockTag(server, "items", NamespacedKey.minecraft("stone_tool_materials"), Material.class, - List.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)); - TagMocks.mockTag(server, "blocks", NamespacedKey.minecraft("planks"), Material.class, - List.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)); //etc. non-exhaustive list + Tag tag = Tag.ITEMS_STONE_TOOL_MATERIALS; + doReturn(Set.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)) + .when(tag).getValues(); + tag = Tag.PLANKS; + doReturn(Set.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)) //etc. non-exhaustive list + .when(tag).getValues(); - Bukkit.setServer(server); EnchantmentMocks.init(server); } diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java index 00274ee..82c9bb9 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java @@ -6,21 +6,20 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.not; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import com.github.jikoo.planarenchanting.util.mock.ServerMocks; -import com.github.jikoo.planarenchanting.util.mock.TagMocks; import com.github.jikoo.planarenchanting.util.mock.enchantments.EnchantmentMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.InventoryMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.ItemFactoryMocks; -import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.stream.Stream; -import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.Server; +import org.bukkit.Tag; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.ItemFactory; @@ -61,15 +60,18 @@ void beforeAll() { when(server.getItemFactory()).thenReturn(factory); // RepairMaterial requires these tags to be set up to test. - TagMocks.mockTag(server, "items", NamespacedKey.minecraft("stone_tool_materials"), Material.class, - List.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)); - TagMocks.mockTag(server, "blocks", NamespacedKey.minecraft("planks"), Material.class, - List.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)); //etc. non-exhaustive list + Tag tag = Tag.ITEMS_STONE_TOOL_MATERIALS; + doReturn(Set.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)) + .when(tag).getValues(); + tag = Tag.PLANKS; + doReturn(Set.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)) //etc. non-exhaustive list + .when(tag).getValues(); - Bukkit.setServer(server); EnchantmentMocks.init(server); - toolEnchantment = Enchantment.DIG_SPEED; + toolEnchantment = Enchantment.EFFICIENCY; + tag = Tag.ITEMS_ENCHANTABLE_MINING; + doReturn(Set.of(TOOL)).when(tag).getValues(); } @BeforeEach @@ -95,7 +97,7 @@ void testEnchantmentTarget() { @Test void testEnchantmentConflict() { Enchantment conflict1 = Enchantment.SILK_TOUCH; - Enchantment conflict2 = Enchantment.LOOT_BONUS_BLOCKS; + Enchantment conflict2 = Enchantment.FORTUNE; assertThat( "Vanilla enchantments conflict", diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java index a06f98f..9b626cd 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java @@ -10,23 +10,27 @@ import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import com.github.jikoo.planarenchanting.anvil.mock.ReadableResultState; import com.github.jikoo.planarenchanting.enchant.EnchantData; import com.github.jikoo.planarenchanting.enchant.EnchantmentUtil; import com.github.jikoo.planarenchanting.util.mock.ServerMocks; import com.github.jikoo.planarenchanting.util.mock.enchantments.EnchantmentMocks; +import com.github.jikoo.planarenchanting.util.mock.impl.InternalObject; import com.github.jikoo.planarenchanting.util.mock.inventory.InventoryMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.ItemFactoryMocks; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.function.BiConsumer; -import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.Server; +import org.bukkit.Tag; import org.bukkit.enchantments.Enchantment; -import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; @@ -58,19 +62,33 @@ void beforeAll() { ItemFactory factory = ItemFactoryMocks.mockFactory(); when(server.getItemFactory()).thenReturn(factory); - Bukkit.setServer(server); - EnchantmentMocks.init(server); - basicEnchant = Enchantment.DIG_SPEED; - rareEnchant = Enchantment.LOOT_BONUS_BLOCKS; - commonTridentEnchant = Enchantment.PROTECTION_ENVIRONMENTAL; + Tag tag = Tag.ITEMS_ENCHANTABLE_TRIDENT; + doReturn(Set.of(Material.TRIDENT)).when(tag).getValues(); + doReturn(true).when(tag).isTagged(Material.TRIDENT); + + basicEnchant = Enchantment.EFFICIENCY; + rareEnchant = Enchantment.FORTUNE; tridentEnchant = Enchantment.RIPTIDE; - // Set up protection as a trident enchantment. + // Set up a fake common-rarity trident enchantment. // This is necessary because no actual trident enchantments are currently common, so the // defensive code will not be hit in testing normally. - doReturn(EnchantmentTarget.TRIDENT).when(commonTridentEnchant).getItemTarget(); + var commonTrident = (Enchantment & InternalObject) mock(Enchantment.class, withSettings().extraInterfaces(InternalObject.class)); + NamespacedKey key = NamespacedKey.minecraft("common_trident"); + doReturn(key).when(commonTrident).getKey(); + doReturn( + new net.minecraft.world.item.enchantment.Enchantment( + key, + value -> 1, + value -> 21, + 10, + 1 + ) + ).when(commonTrident).getHandle(); + EnchantmentMocks.putEnchant(commonTrident); + commonTridentEnchant = commonTrident; } abstract static class CombineEnchantsTest { @@ -117,7 +135,7 @@ void testNoEnchantsAdded() { @Test void testBasicAdd() { Map enchantments = new HashMap<>(); - enchantments.put(Enchantment.DIG_SPEED, 1); + enchantments.put(Enchantment.EFFICIENCY, 1); ItemStack addition = new ItemStack(BASE_MAT); applyEnchantments(addition, enchantments); @@ -147,7 +165,7 @@ void testBookHalfPrice() { EnchantData enchantData = EnchantData.of(rareEnchant); assertThat( "Enchantment must have rarity > 1", - enchantData.getRarity().getAnvilValue(), + enchantData.getAnvilCost(), greaterThan(1)); diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/RepairMaterialTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/RepairMaterialTest.java index 2d66bf6..95a9baa 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/RepairMaterialTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/RepairMaterialTest.java @@ -2,14 +2,12 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.doReturn; import com.github.jikoo.planarenchanting.util.mock.ServerMocks; -import com.github.jikoo.planarenchanting.util.mock.TagMocks; -import java.util.List; -import org.bukkit.Bukkit; +import java.util.Set; import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Server; +import org.bukkit.Tag; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInstance; @@ -23,15 +21,15 @@ class RepairMaterialTest { @BeforeAll void beforeAll() { - Server server = ServerMocks.mockServer(); + ServerMocks.mockServer(); // RepairMaterial requires these tags. - TagMocks.mockTag(server, "items", NamespacedKey.minecraft("stone_tool_materials"), Material.class, - List.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)); - TagMocks.mockTag(server, "blocks", NamespacedKey.minecraft("planks"), Material.class, - List.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)); //etc. non-exhaustive list - - Bukkit.setServer(server); + Tag tag = Tag.ITEMS_STONE_TOOL_MATERIALS; + doReturn(Set.of(Material.STONE, Material.ANDESITE, Material.GRANITE, Material.DIORITE)) + .when(tag).getValues(); + tag = Tag.PLANKS; + doReturn(Set.of(Material.ACACIA_PLANKS, Material.BIRCH_PLANKS, Material.OAK_PLANKS)) //etc. non-exhaustive list + .when(tag).getValues(); } @ParameterizedTest @@ -54,7 +52,8 @@ private static boolean isRepairable(@NotNull Material material) { SHEARS, TRIDENT, CROSSBOW, - BRUSH -> false; + BRUSH, + WOLF_ARMOR-> false; default -> material.getMaxDurability() > 0; }; } diff --git a/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantDataTest.java b/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantDataTest.java index 72d00f9..591949f 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantDataTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantDataTest.java @@ -6,12 +6,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.withSettings; -import com.github.jikoo.planarenchanting.util.mock.impl.InternalObject; import com.github.jikoo.planarenchanting.util.mock.ServerMocks; import com.github.jikoo.planarenchanting.util.mock.enchantments.EnchantmentMocks; +import com.github.jikoo.planarenchanting.util.mock.impl.InternalObject; import java.util.stream.Stream; -import net.minecraft.world.item.enchantment.Enchantment.Rarity; -import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.Server; @@ -30,7 +28,6 @@ class EnchantDataTest { @BeforeAll void beforeAll() { Server server = ServerMocks.mockServer(); - Bukkit.setServer(server); EnchantmentMocks.init(server); } @@ -47,7 +44,7 @@ void testUnknownEnchant() { var enchant = (Enchantment & InternalObject) mock(Enchantment.class, withSettings().extraInterfaces(InternalObject.class)); NamespacedKey key = NamespacedKey.minecraft("fake_enchant"); doReturn(key).when(enchant).getKey(); - doReturn(new net.minecraft.world.item.enchantment.Enchantment(key, value -> 5, value -> 10, new Rarity(5))) + doReturn(new net.minecraft.world.item.enchantment.Enchantment(key, value -> 5, value -> 10, 5, 2)) .when(enchant).getHandle(); EnchantmentMocks.putEnchant(enchant); @@ -55,7 +52,7 @@ void testUnknownEnchant() { assertThat("Backing enchantment is identical", data.getEnchantment(), is(enchant)); assertThat("Weight is expected value", data.getWeight(), is(5)); - assertThat("Rarity is expected value", data.getRarity(), is(EnchantRarity.UNCOMMON)); + assertThat("Anvil cost is expected value", data.getAnvilCost(), is(2)); assertThat("Min quality is expected value", data.getMinCost(0), is(5)); assertThat("Max quality is expected value", data.getMaxCost(0), is(10)); } diff --git a/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentReflectionTest.java b/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentReflectionTest.java index eee8307..7506ab4 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentReflectionTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentReflectionTest.java @@ -12,8 +12,6 @@ import java.lang.reflect.InvocationTargetException; import java.util.stream.IntStream; import java.util.stream.Stream; -import net.minecraft.world.item.enchantment.Enchantment.Rarity; -import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.Server; @@ -47,7 +45,6 @@ class EnchantmentReflectionTest { @BeforeAll void beforeAll() { Server server = ServerMocks.mockServer(); - Bukkit.setServer(server); EnchantmentMocks.init(server); Registry.ENCHANTMENT.stream().map(enchantment -> { @@ -63,7 +60,7 @@ void beforeAll() { // Don't need to copy rest of data over EnchantData data = EnchantData.of(enchantment); doReturn(new net.minecraft.world.item.enchantment.Enchantment( - enchantment.getKey(), data::getMinCost, data::getMaxCost, new Rarity(data.getWeight()))) + enchantment.getKey(), data::getMinCost, data::getMaxCost, data.getWeight(), data.getAnvilCost())) .when(internalEnchant).getHandle(); return internalEnchant; @@ -83,7 +80,8 @@ void beforeAll() { Unsafe.getUnsafe().throwException(new InvocationTargetException(new Exception("hello world"))); return 5; }, - new Rarity(-5) + 0, + 40 ) ).when(brokenInternal).getHandle(); EnchantmentMocks.putEnchant(brokenInternal); @@ -127,36 +125,47 @@ void testReflectiveMax(Enchantment enchantment, int level) { is(EnchantDataReflection.getMaxCost(enchantment).applyAsInt(level))); } - @DisplayName("Reflection should grab minimum method or fall through gracefully.") + @DisplayName("Reflection should grab weight or fall through gracefully.") @ParameterizedTest @MethodSource("streamEnchants") - void testReflectiveRarity(Enchantment enchantment) { + void testReflectiveWeight(Enchantment enchantment) { EnchantData enchantData = EnchantData.of(enchantment); assertThat("Reflection should provide expected value", - enchantData.getRarity(), is(EnchantDataReflection.getRarity(enchantment))); + enchantData.getWeight(), is(EnchantDataReflection.getWeight(enchantment))); + } + + @DisplayName("Reflection should grab anvil cost multiplier or fall through gracefully.") + @ParameterizedTest + @MethodSource("streamEnchants") + void testReflectiveAnvilCost(Enchantment enchantment) { + EnchantData enchantData = EnchantData.of(enchantment); + assertThat("Reflection should provide expected value", + enchantData.getAnvilCost(), is(EnchantDataReflection.getAnvilCost(enchantment))); } @DisplayName("Reflection should provide usable defaults.") @ParameterizedTest @MethodSource("getBrokenEnchants") void testReflectiveDefaults(Enchantment broken) { - assertThat("Reflection should fall through gracefully", 1, - is(EnchantDataReflection.getMinCost(broken).applyAsInt(0))); - assertThat("Reflection should fall through gracefully", 21, - is(EnchantDataReflection.getMinCost(broken).applyAsInt(2))); - assertThat("Reflection should fall through gracefully", 6, - is(EnchantDataReflection.getMaxCost(broken).applyAsInt(0))); - assertThat("Reflection should fall through gracefully", 26, - is(EnchantDataReflection.getMaxCost(broken).applyAsInt(2))); - assertThat("Reflection should fall through gracefully", 0, - is(EnchantDataReflection.getRarity(broken).getWeight())); + assertThat("Reflection should fall through gracefully", + EnchantDataReflection.getMinCost(broken).applyAsInt(0), is(1)); + assertThat("Reflection should fall through gracefully", + EnchantDataReflection.getMinCost(broken).applyAsInt(2), is(21)); + assertThat("Reflection should fall through gracefully", + EnchantDataReflection.getMaxCost(broken).applyAsInt(0), is(6)); + assertThat("Reflection should fall through gracefully", + EnchantDataReflection.getMaxCost(broken).applyAsInt(2), is(26)); + assertThat("Reflection should fall through gracefully", + EnchantDataReflection.getWeight(broken), is(0)); + assertThat("Reflection should fall through gracefully", + EnchantDataReflection.getAnvilCost(broken), is(40)); } public Stream> getBrokenEnchants() { var validUnregisteredEnchant = (Enchantment & InternalObject) mock(Enchantment.class, withSettings().extraInterfaces(InternalObject.class)); NamespacedKey key = NamespacedKey.minecraft("fake_enchant2"); doReturn(key).when(validUnregisteredEnchant).getKey(); - doReturn(new net.minecraft.world.item.enchantment.Enchantment(key, value -> 5, value -> 10, new Rarity(5))) + doReturn(new net.minecraft.world.item.enchantment.Enchantment(key, value -> 5, value -> 10, 5, 20)) .when(validUnregisteredEnchant).getHandle(); return Stream.of( diff --git a/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentUtilTest.java b/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentUtilTest.java index d5027fc..af6cbf4 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentUtilTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/enchant/EnchantmentUtilTest.java @@ -14,7 +14,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.meta.EnchantmentStorageMeta; @@ -32,7 +31,6 @@ class EnchantmentUtilTest { @BeforeAll void beforeAll() { Server server = ServerMocks.mockServer(); - Bukkit.setServer(server); EnchantmentMocks.init(server); } @@ -48,8 +46,8 @@ void testGetEnchantsStorageMeta() { assertThat("Meta is empty", EnchantmentUtil.getEnchants(meta), is(anEmptyMap())); Map enchantments = new HashMap<>(); - enchantments.put(Enchantment.DIG_SPEED, 10); - enchantments.put(Enchantment.LUCK, 5); + enchantments.put(Enchantment.EFFICIENCY, 10); + enchantments.put(Enchantment.LUCK_OF_THE_SEA, 5); for (Entry enchant : enchantments.entrySet()) { meta.addStoredEnchant(enchant.getKey(), enchant.getValue(), true); @@ -68,8 +66,8 @@ void testSetEnchantsStorageMeta() { assertThat("Meta is empty", EnchantmentUtil.getEnchants(meta), is(anEmptyMap())); Map enchantments = new HashMap<>(); - enchantments.put(Enchantment.DIG_SPEED, 10); - enchantments.put(Enchantment.LUCK, 5); + enchantments.put(Enchantment.EFFICIENCY, 10); + enchantments.put(Enchantment.LUCK_OF_THE_SEA, 5); EnchantmentUtil.addEnchants(meta, enchantments); @@ -86,8 +84,8 @@ void testGetEnchants() { assertThat("Meta is empty", EnchantmentUtil.getEnchants(meta), is(anEmptyMap())); Map enchantments = new HashMap<>(); - enchantments.put(Enchantment.DIG_SPEED, 10); - enchantments.put(Enchantment.LUCK, 5); + enchantments.put(Enchantment.EFFICIENCY, 10); + enchantments.put(Enchantment.LUCK_OF_THE_SEA, 5); for (Entry enchant : enchantments.entrySet()) { meta.addEnchant(enchant.getKey(), enchant.getValue(), true); @@ -106,8 +104,8 @@ void testSetEnchants() { assertThat("Meta is empty", EnchantmentUtil.getEnchants(meta), is(anEmptyMap())); Map enchantments = new HashMap<>(); - enchantments.put(Enchantment.DIG_SPEED, 10); - enchantments.put(Enchantment.LUCK, 5); + enchantments.put(Enchantment.EFFICIENCY, 10); + enchantments.put(Enchantment.LUCK_OF_THE_SEA, 5); EnchantmentUtil.addEnchants(meta, enchantments); diff --git a/src/test/java/com/github/jikoo/planarenchanting/table/EnchantabilityTest.java b/src/test/java/com/github/jikoo/planarenchanting/table/EnchantabilityTest.java index ba34c7f..fca3362 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/table/EnchantabilityTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/table/EnchantabilityTest.java @@ -35,7 +35,8 @@ private static boolean isTableEnchantable(Material material) { return switch (material) { // Enchanted book is technically not enchantable but for simplicity it's included. case BOOK, ENCHANTED_BOOK -> true; - case CARROT_ON_A_STICK, WARPED_FUNGUS_ON_A_STICK, ELYTRA, FLINT_AND_STEEL, SHEARS, BRUSH -> + case CARROT_ON_A_STICK, WARPED_FUNGUS_ON_A_STICK, ELYTRA, FLINT_AND_STEEL, SHEARS, BRUSH, + WOLF_ARMOR -> false; default -> Arrays.stream(EnchantmentTarget.values()) // Vanishable/wearable include items that cannot be enchanted except via anvils. diff --git a/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableTest.java b/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableTest.java index 690fb2c..de814ab 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableTest.java @@ -14,7 +14,6 @@ import java.util.List; import java.util.Map; import java.util.Random; -import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.enchantments.Enchantment; import org.jetbrains.annotations.NotNull; @@ -48,12 +47,11 @@ class EnchantingTableTest { @BeforeAll void beforeAll() { Server server = ServerMocks.mockServer(); - Bukkit.setServer(server); EnchantmentMocks.init(server); toolEnchants = List.of( - Enchantment.DIG_SPEED, - Enchantment.DURABILITY, - Enchantment.LOOT_BONUS_BLOCKS, + Enchantment.EFFICIENCY, + Enchantment.UNBREAKING, + Enchantment.FORTUNE, Enchantment.SILK_TOUCH); } @@ -83,7 +81,7 @@ void testAllIncompatibleAlwaysSingle() { @DisplayName("Enchantment level max can be modified.") @Test void testSetMaxLevel() { - Enchantment enchant = Enchantment.DIG_SPEED; + Enchantment enchant = Enchantment.EFFICIENCY; var operation = new EnchantingTable(List.of(enchant), Enchantability.GOLD_ARMOR); // Double max level for enchants that go over 1. operation.setMaxLevel(enchant1 -> enchant1.getMaxLevel() > 1 ? enchant1.getMaxLevel() * 2 : 1); diff --git a/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableUtilTest.java b/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableUtilTest.java index 3e45aeb..de89c0a 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableUtilTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/table/EnchantingTableUtilTest.java @@ -34,7 +34,6 @@ import org.bukkit.Server; import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.EnchantmentOffer; -import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView.Property; @@ -56,7 +55,6 @@ class EnchantingTableUtilTest { @BeforeAll void beforeAll() { Server server = ServerMocks.mockServer(); - Bukkit.setServer(server); EnchantmentMocks.init(server); } @@ -105,8 +103,8 @@ void testSendButtonUpdates() { }); EnchantmentOffer[] offers = new EnchantmentOffer[] { - new EnchantmentOffer(Enchantment.DIG_SPEED, 5, 1), - new EnchantmentOffer(Enchantment.DURABILITY, 20, 2), + new EnchantmentOffer(Enchantment.EFFICIENCY, 5, 1), + new EnchantmentOffer(Enchantment.UNBREAKING, 20, 2), new EnchantmentOffer(Enchantment.SILK_TOUCH, 30, 3) }; @@ -206,7 +204,6 @@ private Enchantment enchantment(String key) { doReturn(NamespacedKey.minecraft(key)).when(enchantment).getKey(); doReturn(1).when(enchantment).getStartLevel(); doReturn(1).when(enchantment).getMaxLevel(); - doReturn(EnchantmentTarget.VANISHABLE).when(enchantment).getItemTarget(); return enchantment; } diff --git a/src/test/java/com/github/jikoo/planarenchanting/table/TableEnchantListenerTest.java b/src/test/java/com/github/jikoo/planarenchanting/table/TableEnchantListenerTest.java index 7d74cb9..b402d2f 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/table/TableEnchantListenerTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/table/TableEnchantListenerTest.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -84,14 +83,13 @@ void setUpAll() { }); when(server.getScheduler()).thenReturn(scheduler); - Bukkit.setServer(server); EnchantmentMocks.init(server); - validEnchant = Enchantment.DIG_SPEED; + validEnchant = Enchantment.EFFICIENCY; toolEnchants = List.of( - Enchantment.DIG_SPEED, - Enchantment.DURABILITY, - Enchantment.LOOT_BONUS_BLOCKS, + Enchantment.EFFICIENCY, + Enchantment.UNBREAKING, + Enchantment.FORTUNE, Enchantment.SILK_TOUCH); } @@ -281,7 +279,7 @@ void testLegacySeedUnsetPostEnchant() { itemStack, level, new HashMap<>(), - Enchantment.DURABILITY, + Enchantment.UNBREAKING, 0, buttonIndex); } diff --git a/src/test/java/com/github/jikoo/planarenchanting/util/mock/ServerMocks.java b/src/test/java/com/github/jikoo/planarenchanting/util/mock/ServerMocks.java index 0bdeae3..cfc86f8 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/util/mock/ServerMocks.java +++ b/src/test/java/com/github/jikoo/planarenchanting/util/mock/ServerMocks.java @@ -1,14 +1,24 @@ package com.github.jikoo.planarenchanting.util.mock; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import java.lang.reflect.Field; +import java.util.Set; import java.util.logging.Logger; import org.bukkit.Bukkit; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.Server; +import org.bukkit.Tag; import org.jetbrains.annotations.NotNull; +import org.mockito.Answers; public final class ServerMocks { @@ -18,6 +28,47 @@ public final class ServerMocks { Logger noOp = mock(Logger.class); when(mock.getLogger()).thenReturn(noOp); + // Server must be available before tags can be mocked. + Bukkit.setServer(mock); + + // Bukkit has a lot of static constants referencing registry values. To initialize those, the + // registries must be able to be fetched before the classes are touched. + // The mock registry can later be more specifically modified as necessary. + doAnswer(invocationGetRegistry -> { + // This must be mocked here or else Registry will be initialized when mocking it. + Registry registry = mock(); + doAnswer(invocationGetEntry -> { + NamespacedKey key = invocationGetEntry.getArgument(0); + // Set registries to always return a new value so that any constants are initialized. + Class arg = invocationGetRegistry.getArgument(0); + // Deep stubs aren't great, but Bukkit has a lot of nullity checks on new constants. + Keyed keyed = mock(arg, withSettings().defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + doReturn(key).when(keyed).getKey(); + // It may eventually be necessary to stub BlockType#typed() here, but deep stubs work for now. + return keyed; + }).when(registry).get(notNull()); + return registry; + }).when(mock).getRegistry(notNull()); + + // Tags are dependent on registries, but use a different method. + // This will set up blank tags for each constant; all that needs to be done to render them + // functional is to re-mock Tag#getValues. + doAnswer(invocationGetTag -> { + Tag tag = mock(); + doReturn(invocationGetTag.getArgument(1)).when(tag).getKey(); + doReturn(Set.of()).when(tag).getValues(); + doAnswer(invocationIsTagged -> { + Keyed keyed = invocationIsTagged.getArgument(0); + Class type = invocationGetTag.getArgument(2); + if (!type.isAssignableFrom(keyed.getClass())) { + return null; + } + // Since these are mocks, the exact instance might not be equal. Consider equal keys equal. + return tag.getValues().contains(keyed) || tag.getValues().stream().anyMatch(value -> value.getKey().equals(keyed.getKey())); + }).when(tag).isTagged(notNull()); + return tag; + }).when(mock).getTag(notNull(), notNull(), notNull()); + return mock; } diff --git a/src/test/java/com/github/jikoo/planarenchanting/util/mock/TagMocks.java b/src/test/java/com/github/jikoo/planarenchanting/util/mock/TagMocks.java deleted file mode 100644 index b35a5ea..0000000 --- a/src/test/java/com/github/jikoo/planarenchanting/util/mock/TagMocks.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.jikoo.planarenchanting.util.mock; - -import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Collection; -import java.util.Set; -import org.bukkit.Keyed; -import org.bukkit.NamespacedKey; -import org.bukkit.Server; -import org.bukkit.Tag; -import org.jetbrains.annotations.NotNull; - -public final class TagMocks { - - public static void mockTag( - @NotNull Server server, - @NotNull String registry, - @NotNull NamespacedKey key, - @NotNull Class clazz, - Collection values) { - - // Server should already be a mock - when(server.getTag(registry, key, clazz)).thenAnswer(invocation -> { - Tag mock = mock(Tag.class); - - when(mock.getKey()).thenReturn(key); - when(mock.isTagged(notNull())).thenAnswer(invocation1 -> { - T argument = invocation1.getArgument(0); - return values.contains(argument); - }); - when(mock.getValues()).thenAnswer(invocation1 -> Set.copyOf(values)); - - return mock; - }); - } - - private TagMocks() {} - -} diff --git a/src/test/java/com/github/jikoo/planarenchanting/util/mock/enchantments/EnchantmentMocks.java b/src/test/java/com/github/jikoo/planarenchanting/util/mock/enchantments/EnchantmentMocks.java index d630953..891eae4 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/util/mock/enchantments/EnchantmentMocks.java +++ b/src/test/java/com/github/jikoo/planarenchanting/util/mock/enchantments/EnchantmentMocks.java @@ -1,11 +1,10 @@ package com.github.jikoo.planarenchanting.util.mock.enchantments; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import com.github.jikoo.planarenchanting.util.ItemUtil; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; @@ -14,12 +13,12 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.bukkit.Keyed; +import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.Server; +import org.bukkit.Tag; import org.bukkit.enchantments.Enchantment; -import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.mockito.ArgumentMatchers; @@ -28,89 +27,66 @@ public class EnchantmentMocks { private static final Map KEYS_TO_ENCHANTS = new HashMap<>(); public static void init(Server server) { - // Horrible load order mess: - // Enchantments load as soon as the Enchantment class is initialized, which happens when - // Registry.ENCHANTMENT is initialized during Registry initialization. This means that we must - // be prepared to initialize enchantments before we can initialize the enchantment registry at - // all, and can reference neither class until then. - doAnswer(invocationGetRegistry -> { - // This must be mocked here or else Registry will be initialized when mocking it. - Registry registry = mock(); - doAnswer(invocationGetEntry -> { - NamespacedKey key = invocationGetEntry.getArgument(0); - // Set registries to always return a new value. - // For enchantments, this allows us to use Bukkit's up-to-date namespaced keys instead of - // maintaining a listing. - Class arg = invocationGetRegistry.getArgument(0); - Keyed keyed = mock(arg); - doReturn(key).when(keyed).getKey(); - return keyed; - }).when(registry).get(notNull()); - return registry; - }).when(server).getRegistry(notNull()); - Registry registry = Registry.ENCHANTMENT; // Redirect enchantment registry back so that our modified version is always returned. doReturn(registry).when(server).getRegistry(Enchantment.class); - setUpEnchant(Enchantment.PROTECTION_ENVIRONMENTAL, 4, EnchantmentTarget.ARMOR, - List.of(Enchantment.PROTECTION_FIRE, Enchantment.PROTECTION_EXPLOSIONS, Enchantment.PROTECTION_PROJECTILE)); - setUpEnchant(Enchantment.PROTECTION_FIRE, 4, EnchantmentTarget.ARMOR, - List.of(Enchantment.PROTECTION_ENVIRONMENTAL, Enchantment.PROTECTION_EXPLOSIONS, Enchantment.PROTECTION_PROJECTILE)); - setUpEnchant(Enchantment.PROTECTION_FALL, 4, EnchantmentTarget.ARMOR_FEET); - setUpEnchant(Enchantment.PROTECTION_EXPLOSIONS, 4, EnchantmentTarget.ARMOR, - List.of(Enchantment.PROTECTION_ENVIRONMENTAL, Enchantment.PROTECTION_FIRE, Enchantment.PROTECTION_PROJECTILE)); - setUpEnchant(Enchantment.PROTECTION_PROJECTILE, 4, EnchantmentTarget.ARMOR, - List.of(Enchantment.PROTECTION_ENVIRONMENTAL, Enchantment.PROTECTION_FIRE, Enchantment.PROTECTION_EXPLOSIONS)); - - setUpEnchant(Enchantment.OXYGEN, 3, EnchantmentTarget.ARMOR_HEAD); - setUpEnchant(Enchantment.WATER_WORKER, 1, EnchantmentTarget.ARMOR_HEAD); + List protections = List.of(Enchantment.PROTECTION, Enchantment.FIRE_PROTECTION, Enchantment.BLAST_PROTECTION, Enchantment.PROJECTILE_PROTECTION); + setUpEnchant(Enchantment.PROTECTION, 4, Tag.ITEMS_ENCHANTABLE_ARMOR, protections); + setUpEnchant(Enchantment.FIRE_PROTECTION, 4, Tag.ITEMS_ENCHANTABLE_ARMOR, protections); + setUpEnchant(Enchantment.FEATHER_FALLING, 4, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR); + setUpEnchant(Enchantment.BLAST_PROTECTION, 4, Tag.ITEMS_ENCHANTABLE_ARMOR, protections); + setUpEnchant(Enchantment.PROJECTILE_PROTECTION, 4, Tag.ITEMS_ENCHANTABLE_ARMOR, protections); - setUpEnchant(Enchantment.THORNS, 3, EnchantmentTarget.ARMOR); + setUpEnchant(Enchantment.RESPIRATION, 3, Tag.ITEMS_ENCHANTABLE_HEAD_ARMOR); + setUpEnchant(Enchantment.AQUA_AFFINITY, 1, Tag.ITEMS_ENCHANTABLE_HEAD_ARMOR); - setUpEnchant(Enchantment.DEPTH_STRIDER, 3, EnchantmentTarget.ARMOR_FEET, List.of(Enchantment.FROST_WALKER)); - setUpEnchant(Enchantment.FROST_WALKER, 3, EnchantmentTarget.ARMOR_FEET, true, List.of(Enchantment.DEPTH_STRIDER)); + setUpEnchant(Enchantment.THORNS, 3, Tag.ITEMS_ENCHANTABLE_ARMOR); - setUpEnchant(Enchantment.BINDING_CURSE, 1, EnchantmentTarget.WEARABLE, true, List.of()); + setUpEnchant(Enchantment.DEPTH_STRIDER, 3, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, List.of(Enchantment.FROST_WALKER)); + setUpEnchant(Enchantment.FROST_WALKER, 3, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, List.of(Enchantment.DEPTH_STRIDER)); + setUpEnchant(Enchantment.SOUL_SPEED, 3, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_FOOT_ARMOR, List.of()); + setUpEnchant(Enchantment.SWIFT_SNEAK, 3, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_LEG_ARMOR, List.of()); - setUpEnchant(Enchantment.DAMAGE_ALL, 5, EnchantmentTarget.WEAPON, List.of(Enchantment.DAMAGE_ARTHROPODS, Enchantment.DAMAGE_UNDEAD)); - setUpEnchant(Enchantment.DAMAGE_UNDEAD, 5, EnchantmentTarget.WEAPON, List.of(Enchantment.DAMAGE_ALL, Enchantment.DAMAGE_ARTHROPODS)); - setUpEnchant(Enchantment.DAMAGE_ARTHROPODS, 5, EnchantmentTarget.WEAPON, List.of(Enchantment.DAMAGE_ALL, Enchantment.DAMAGE_UNDEAD)); - setUpEnchant(Enchantment.KNOCKBACK, 2, EnchantmentTarget.WEAPON); - setUpEnchant(Enchantment.FIRE_ASPECT, 2, EnchantmentTarget.WEAPON); - setUpEnchant(Enchantment.LOOT_BONUS_MOBS, 3, EnchantmentTarget.WEAPON); - setUpEnchant(Enchantment.SWEEPING_EDGE, 3, EnchantmentTarget.WEAPON); + setUpEnchant(Enchantment.BINDING_CURSE, 1, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_EQUIPPABLE, List.of()); - setUpEnchant(Enchantment.DIG_SPEED, 5, EnchantmentTarget.TOOL); - setUpEnchant(Enchantment.SILK_TOUCH, 1, EnchantmentTarget.TOOL, List.of(Enchantment.LOOT_BONUS_BLOCKS)); + setUpEnchant(Enchantment.SHARPNESS, 5, Tag.ITEMS_ENCHANTABLE_SWORD, Tag.ITEMS_ENCHANTABLE_SHARP_WEAPON, List.of(Enchantment.BANE_OF_ARTHROPODS, Enchantment.SMITE)); + setUpEnchant(Enchantment.SMITE, 5, Tag.ITEMS_ENCHANTABLE_SWORD, Tag.ITEMS_ENCHANTABLE_WEAPON, List.of(Enchantment.SHARPNESS, Enchantment.BANE_OF_ARTHROPODS)); + setUpEnchant(Enchantment.BANE_OF_ARTHROPODS, 5, Tag.ITEMS_ENCHANTABLE_SWORD, Tag.ITEMS_ENCHANTABLE_WEAPON, List.of(Enchantment.SHARPNESS, Enchantment.SMITE)); + setUpEnchant(Enchantment.KNOCKBACK, 2, Tag.ITEMS_ENCHANTABLE_SWORD); + setUpEnchant(Enchantment.FIRE_ASPECT, 2, Tag.ITEMS_ENCHANTABLE_FIRE_ASPECT); + setUpEnchant(Enchantment.LOOTING, 3, Tag.ITEMS_ENCHANTABLE_SWORD); + setUpEnchant(Enchantment.SWEEPING_EDGE, 3, Tag.ITEMS_ENCHANTABLE_SWORD); - setUpEnchant(Enchantment.DURABILITY, 3, EnchantmentTarget.BREAKABLE); + setUpEnchant(Enchantment.EFFICIENCY, 5, Tag.ITEMS_ENCHANTABLE_MINING); + setUpEnchant(Enchantment.SILK_TOUCH, 1, Tag.ITEMS_ENCHANTABLE_MINING_LOOT, List.of(Enchantment.FORTUNE)); - setUpEnchant(Enchantment.LOOT_BONUS_BLOCKS, 3, EnchantmentTarget.TOOL, List.of(Enchantment.SILK_TOUCH)); + setUpEnchant(Enchantment.UNBREAKING, 3, Tag.ITEMS_ENCHANTABLE_DURABILITY); - setUpEnchant(Enchantment.ARROW_DAMAGE, 5, EnchantmentTarget.BOW); - setUpEnchant(Enchantment.ARROW_KNOCKBACK, 2, EnchantmentTarget.BOW); - setUpEnchant(Enchantment.ARROW_FIRE, 1, EnchantmentTarget.BOW); - setUpEnchant(Enchantment.ARROW_INFINITE, 1, EnchantmentTarget.BOW, List.of(Enchantment.MENDING)); + setUpEnchant(Enchantment.FORTUNE, 3, Tag.ITEMS_ENCHANTABLE_MINING_LOOT, List.of(Enchantment.SILK_TOUCH)); - setUpEnchant(Enchantment.LUCK, 3, EnchantmentTarget.FISHING_ROD); - setUpEnchant(Enchantment.LURE, 3, EnchantmentTarget.FISHING_ROD); + setUpEnchant(Enchantment.POWER, 5, Tag.ITEMS_ENCHANTABLE_BOW); + setUpEnchant(Enchantment.PUNCH, 2, Tag.ITEMS_ENCHANTABLE_BOW); + setUpEnchant(Enchantment.FLAME, 1, Tag.ITEMS_ENCHANTABLE_BOW); + setUpEnchant(Enchantment.INFINITY, 1, Tag.ITEMS_ENCHANTABLE_BOW, List.of(Enchantment.MENDING)); - setUpEnchant(Enchantment.LOYALTY, 3, EnchantmentTarget.TRIDENT, List.of(Enchantment.RIPTIDE)); - setUpEnchant(Enchantment.IMPALING, 5, EnchantmentTarget.TRIDENT); - setUpEnchant(Enchantment.RIPTIDE, 3, EnchantmentTarget.TRIDENT, List.of(Enchantment.CHANNELING, Enchantment.LOYALTY)); - setUpEnchant(Enchantment.CHANNELING, 1, EnchantmentTarget.TRIDENT, List.of(Enchantment.RIPTIDE)); + setUpEnchant(Enchantment.LUCK_OF_THE_SEA, 3, Tag.ITEMS_ENCHANTABLE_FISHING); + setUpEnchant(Enchantment.LURE, 3, Tag.ITEMS_ENCHANTABLE_FISHING); - setUpEnchant(Enchantment.MULTISHOT, 1, EnchantmentTarget.CROSSBOW, List.of(Enchantment.PIERCING)); - setUpEnchant(Enchantment.QUICK_CHARGE, 3, EnchantmentTarget.CROSSBOW); - setUpEnchant(Enchantment.PIERCING, 4, EnchantmentTarget.CROSSBOW, List.of(Enchantment.MULTISHOT)); + setUpEnchant(Enchantment.LOYALTY, 3, Tag.ITEMS_ENCHANTABLE_TRIDENT, List.of(Enchantment.RIPTIDE)); + setUpEnchant(Enchantment.IMPALING, 5, Tag.ITEMS_ENCHANTABLE_TRIDENT); + setUpEnchant(Enchantment.RIPTIDE, 3, Tag.ITEMS_ENCHANTABLE_TRIDENT, List.of(Enchantment.CHANNELING, Enchantment.LOYALTY)); + setUpEnchant(Enchantment.CHANNELING, 1, Tag.ITEMS_ENCHANTABLE_TRIDENT, List.of(Enchantment.RIPTIDE)); - setUpEnchant(Enchantment.MENDING, 1, EnchantmentTarget.BREAKABLE, List.of(Enchantment.ARROW_INFINITE)); + setUpEnchant(Enchantment.MULTISHOT, 1, Tag.ITEMS_ENCHANTABLE_CROSSBOW, List.of(Enchantment.PIERCING)); + setUpEnchant(Enchantment.QUICK_CHARGE, 3, Tag.ITEMS_ENCHANTABLE_CROSSBOW); + setUpEnchant(Enchantment.PIERCING, 4, Tag.ITEMS_ENCHANTABLE_CROSSBOW, List.of(Enchantment.MULTISHOT)); + setUpEnchant(Enchantment.WIND_BURST, 3, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_MACE, List.of()); + setUpEnchant(Enchantment.BREACH, 4, Tag.ITEMS_ENCHANTABLE_MACE); + setUpEnchant(Enchantment.DENSITY, 5, Tag.ITEMS_ENCHANTABLE_MACE); - setUpEnchant(Enchantment.VANISHING_CURSE, 1, EnchantmentTarget.VANISHABLE, true, List.of()); - - setUpEnchant(Enchantment.SOUL_SPEED, 3, EnchantmentTarget.ARMOR_FEET, true, List.of()); - setUpEnchant(Enchantment.SWIFT_SNEAK, 3, EnchantmentTarget.ARMOR_LEGS, true, List.of()); + setUpEnchant(Enchantment.MENDING, 1, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_DURABILITY, List.of(Enchantment.INFINITY)); + setUpEnchant(Enchantment.VANISHING_CURSE, 1, ItemUtil.TAG_EMPTY, Tag.ITEMS_ENCHANTABLE_VANISHING, List.of()); Set missingInternalEnchants = new HashSet<>(); try { @@ -148,38 +124,40 @@ public static void putEnchant(@NotNull Enchantment enchantment) { private static void setUpEnchant( @NotNull Enchantment enchantment, int maxLevel, - @NotNull EnchantmentTarget target) { - setUpEnchant(enchantment, maxLevel, target, false, List.of()); + @NotNull Tag target) { + setUpEnchant(enchantment, maxLevel, target, target, List.of()); } private static void setUpEnchant( @NotNull Enchantment enchantment, int maxLevel, - @NotNull EnchantmentTarget target, + @NotNull Tag target, @NotNull Collection conflicts) { - setUpEnchant(enchantment, maxLevel, target, false, conflicts); + setUpEnchant(enchantment, maxLevel, target, target, conflicts); } private static void setUpEnchant( @NotNull Enchantment enchantment, int maxLevel, - @NotNull EnchantmentTarget target, - boolean treasure, + @NotNull Tag tableTarget, + @NotNull Tag anvilTarget, @NotNull Collection conflicts) { KEYS_TO_ENCHANTS.put(enchantment.getKey(), enchantment); doReturn(1).when(enchantment).getStartLevel(); doReturn(maxLevel).when(enchantment).getMaxLevel(); - doReturn(target).when(enchantment).getItemTarget(); + // Hopefully in the future the enchantment API gets expanded, making separate table+anvil targets available doAnswer(invocation -> { ItemStack item = invocation.getArgument(0); - return item != null && target.includes(item); + return item != null && anvilTarget.isTagged(item.getType()); }).when(enchantment).canEnchantItem(any()); - doReturn(treasure).when(enchantment).isTreasure(); + doReturn(tableTarget.getValues().isEmpty()).when(enchantment).isTreasure(); // Note: Usual implementation allows contains check, but as these are // mocks that cannot be relied on. - doAnswer(invocation -> conflicts.stream().anyMatch(conflict -> conflict.getKey().equals(invocation.getArgument(0, Enchantment.class).getKey()))) - .when(enchantment).conflictsWith(any()); + doAnswer(invocation -> { + NamespacedKey otherKey = invocation.getArgument(0, Enchantment.class).getKey(); + return otherKey.equals(enchantment.getKey()) || conflicts.stream().anyMatch(conflict -> conflict.getKey().equals(otherKey)); + }).when(enchantment).conflictsWith(any()); } } diff --git a/src/test/java/net/minecraft/world/item/enchantment/Enchantment.java b/src/test/java/net/minecraft/world/item/enchantment/Enchantment.java index 00f9da9..0eafbb5 100644 --- a/src/test/java/net/minecraft/world/item/enchantment/Enchantment.java +++ b/src/test/java/net/minecraft/world/item/enchantment/Enchantment.java @@ -7,19 +7,19 @@ public record Enchantment( NamespacedKey key, IntUnaryOperator minCost, IntUnaryOperator maxCost, - Rarity d) { + // NMSREF \nnet\.minecraft\.world\.item\.enchantment\.Enchantment(.|\n)*?int getWeight\(\) + int d, + // NMSREF \nnet\.minecraft\.world\.item\.enchantment\.Enchantment(.|\n)*?int getAnvilCost\(\) + int e) { // NMSREF \nnet\.minecraft\.world\.item\.enchantment\.Enchantment(.|\n)*?int getMinCost\(int\) - public int a(int level) { + public int c(int level) { return minCost().applyAsInt(level); } // NMSREF \nnet\.minecraft\.world\.item\.enchantment\.Enchantment(.|\n)*?int getMaxCost\(int\) - public int b(int level) { + public int d(int level) { return maxCost().applyAsInt(level); } - // NMSREF \nnet\.minecraft\.world\.item\.enchantment\.Enchantment\$Rarity(.|\n)*?int getWeight\(\) - public record Rarity(int a) {} - }