From 4c6f3a4418376a89c34a993d83f1fbef73acff08 Mon Sep 17 00:00:00 2001 From: Brennan Ward Date: Fri, 9 Aug 2024 13:53:27 -0700 Subject: [PATCH] [1.21] Add IItemExtension#supportsEnchantment (#1412) This delegates item<->enchantment compatibility to the item instead of relying on the enchantment's specified item tags. --- .../server/commands/EnchantCommand.java.patch | 11 ++++++ .../world/inventory/AnvilMenu.java.patch | 13 +++++++ .../item/enchantment/Enchantment.java.patch | 34 +++++++++++++++++- .../EnchantRandomlyFunction.java.patch | 11 ++++++ .../common/extensions/IItemExtension.java | 35 +++++++++++++++++-- .../extensions/IItemStackExtension.java | 7 ++++ 6 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 patches/net/minecraft/server/commands/EnchantCommand.java.patch create mode 100644 patches/net/minecraft/world/level/storage/loot/functions/EnchantRandomlyFunction.java.patch diff --git a/patches/net/minecraft/server/commands/EnchantCommand.java.patch b/patches/net/minecraft/server/commands/EnchantCommand.java.patch new file mode 100644 index 0000000000..c9c0649283 --- /dev/null +++ b/patches/net/minecraft/server/commands/EnchantCommand.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/commands/EnchantCommand.java ++++ b/net/minecraft/server/commands/EnchantCommand.java +@@ -81,7 +_,7 @@ + LivingEntity livingentity = (LivingEntity)entity; + ItemStack itemstack = livingentity.getMainHandItem(); + if (!itemstack.isEmpty()) { +- if (enchantment.canEnchant(itemstack) ++ if (itemstack.supportsEnchantment(p_251252_) // Neo: Respect IItemExtension#supportsEnchantment + && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(itemstack).keySet(), p_251252_)) { + itemstack.enchant(p_251252_, p_249941_); + i++; diff --git a/patches/net/minecraft/world/inventory/AnvilMenu.java.patch b/patches/net/minecraft/world/inventory/AnvilMenu.java.patch index 38ae222fff..255c825995 100644 --- a/patches/net/minecraft/world/inventory/AnvilMenu.java.patch +++ b/patches/net/minecraft/world/inventory/AnvilMenu.java.patch @@ -30,6 +30,19 @@ if (itemstack1.isDamageableItem() && itemstack1.getItem().isValidRepairItem(itemstack, itemstack2)) { int l2 = Math.min(itemstack1.getDamageValue(), itemstack1.getMaxDamage() / 4); if (l2 <= 0) { +@@ -176,8 +_,10 @@ + int j2 = entry.getIntValue(); + j2 = i2 == j2 ? j2 + 1 : Math.max(j2, i2); + Enchantment enchantment = holder.value(); +- boolean flag1 = enchantment.canEnchant(itemstack); +- if (this.player.getAbilities().instabuild || itemstack.is(Items.ENCHANTED_BOOK)) { ++ // Neo: Respect IItemExtension#supportsEnchantment - we also delegate the logic for Enchanted Books to this method. ++ // Though we still allow creative players to combine any item with any enchantment in the anvil here. ++ boolean flag1 = itemstack.supportsEnchantment(holder); ++ if (this.player.getAbilities().instabuild) { + flag1 = true; + } + @@ -228,6 +_,7 @@ i += k; itemstack1.remove(DataComponents.CUSTOM_NAME); diff --git a/patches/net/minecraft/world/item/enchantment/Enchantment.java.patch b/patches/net/minecraft/world/item/enchantment/Enchantment.java.patch index 939f9a5229..8237cc26e6 100644 --- a/patches/net/minecraft/world/item/enchantment/Enchantment.java.patch +++ b/patches/net/minecraft/world/item/enchantment/Enchantment.java.patch @@ -1,16 +1,48 @@ --- a/net/minecraft/world/item/enchantment/Enchantment.java +++ b/net/minecraft/world/item/enchantment/Enchantment.java -@@ -132,6 +_,10 @@ +@@ -124,6 +_,10 @@ + return map; + } + ++ /** ++ * @deprecated Neo: Use {@link ItemStack#supportsEnchantment(Holder)} ++ */ ++ @Deprecated + public HolderSet getSupportedItems() { + return this.definition.supportedItems(); + } +@@ -132,10 +_,20 @@ return this.definition.slots().stream().anyMatch(p_345027_ -> p_345027_.test(p_345146_)); } + /** + * @deprecated Neo: Use {@link ItemStack#isPrimaryItemFor(Holder)} ++ * ++ * This method does not respect {@link ItemStack#supportsEnchantment(Holder)} since the {@link Holder} is not available, which makes the result of calling it invalid. + */ + @Deprecated public boolean isPrimaryItem(ItemStack p_336088_) { return this.isSupportedItem(p_336088_) && (this.definition.primaryItems.isEmpty() || p_336088_.is(this.definition.primaryItems.get())); } + ++ /** ++ * @deprecated Neo: Use {@link ItemStack#supportsEnchantment(Holder)} ++ */ ++ @Deprecated + public boolean isSupportedItem(ItemStack p_344865_) { + return p_344865_.is(this.definition.supportedItems); + } +@@ -188,6 +_,10 @@ + return mutablecomponent; + } + ++ /** ++ * @deprecated Neo: Use {@link ItemStack#supportsEnchantment(Holder)} ++ */ ++ @Deprecated + public boolean canEnchant(ItemStack p_44689_) { + return this.definition.supportedItems().contains(p_44689_.getItemHolder()); + } @@ -503,6 +_,15 @@ public static Enchantment.Builder enchantment(Enchantment.EnchantmentDefinition p_345873_) { return new Enchantment.Builder(p_345873_); diff --git a/patches/net/minecraft/world/level/storage/loot/functions/EnchantRandomlyFunction.java.patch b/patches/net/minecraft/world/level/storage/loot/functions/EnchantRandomlyFunction.java.patch new file mode 100644 index 0000000000..57bef15771 --- /dev/null +++ b/patches/net/minecraft/world/level/storage/loot/functions/EnchantRandomlyFunction.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/storage/loot/functions/EnchantRandomlyFunction.java ++++ b/net/minecraft/world/level/storage/loot/functions/EnchantRandomlyFunction.java +@@ -59,7 +_,7 @@ + Stream> stream = this.options + .map(HolderSet::stream) + .orElseGet(() -> p_80430_.getLevel().registryAccess().registryOrThrow(Registries.ENCHANTMENT).holders().map(Function.identity())) +- .filter(p_344686_ -> !flag1 || p_344686_.value().canEnchant(p_80429_)); ++ .filter(p_344686_ -> !flag1 || p_80429_.supportsEnchantment(p_344686_)); // Neo: Respect IItemExtension#supportsEnchantment + List> list = stream.toList(); + Optional> optional = Util.getRandomSafe(list, randomsource); + if (optional.isEmpty()) { diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java index a8ccb67134..1de3e6358d 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java @@ -8,11 +8,13 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup.RegistryLookup; +import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; @@ -45,6 +47,7 @@ import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition; import net.minecraft.world.item.enchantment.EnchantmentInstance; import net.minecraft.world.item.enchantment.ItemEnchantments; import net.minecraft.world.level.Level; @@ -422,17 +425,43 @@ default int getEnchantmentValue(ItemStack stack) { * either from the enchantment table or other random enchantment mechanisms. * As a special case, books are primary items for every enchantment. *

- * Other application mechanisms, such as the anvil, check {@link Enchantment#isSupportedItem(ItemStack)} instead. - * If you want those mechanisms to be able to apply an enchantment, you will need to add your item to the relevant tag. + * Other application mechanisms, such as the anvil, check {@link #supportsEnchantment(ItemStack, Holder)} instead. + * If you want those mechanisms to be able to apply an enchantment, you will need to add your item to the relevant tag or override that method. * * @param stack the item stack to be enchanted * @param enchantment the enchantment to be applied * @return true if this item should be treated as a primary item for the enchantment * @apiNote Call via {@link IItemStackExtension#isPrimaryItemFor(Holder)} + * + * @see #supportsEnchantment(ItemStack, Holder) */ @ApiStatus.OverrideOnly default boolean isPrimaryItemFor(ItemStack stack, Holder enchantment) { - return stack.getItem() == Items.BOOK || enchantment.value().isPrimaryItem(stack); + if (stack.getItem() == Items.BOOK) { + return true; + } + Optional> primaryItems = enchantment.value().definition().primaryItems(); + return this.supportsEnchantment(stack, enchantment) && (primaryItems.isEmpty() || stack.is(primaryItems.get())); + } + + /** + * Checks if the provided enchantment is applicable to the passed item stack. + *

+ * By default, this checks if the {@link EnchantmentDefinition#supportedItems()} contains this item, + * special casing enchanted books as they may receive any enchantment. + *

+ * Overriding this method allows for dynamic logic that would not be possible using the tag system. + * + * @param stack the item stack to be enchanted + * @param enchantment the enchantment to be applied + * @return true if this item can accept the enchantment + * @apiNote Call via {@link IItemStackExtension#supportsEnchantment(Holder)} + * + * @see #isPrimaryItemFor(ItemStack, Holder) + */ + @ApiStatus.OverrideOnly + default boolean supportsEnchantment(ItemStack stack, Holder enchantment) { + return stack.is(Items.ENCHANTED_BOOK) || enchantment.value().isSupportedItem(stack); } /** diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java index 2993635a2b..8c9d46d8e8 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java @@ -140,6 +140,13 @@ default boolean isPrimaryItemFor(Holder enchantment) { return self().getItem().isPrimaryItemFor(self(), enchantment); } + /** + * @see {@link IItemExtension#supportsEnchantment(ItemStack, Holder)} + */ + default boolean supportsEnchantment(Holder enchantment) { + return self().getItem().supportsEnchantment(self(), enchantment); + } + /** * Gets the gameplay level of the target enchantment on this stack. *