Skip to content

Commit 3b1ec89

Browse files
committed
Fix #4003 Crash from isBookEnchantable being removed from NeoForge
See neoforged/NeoForge#2260
1 parent aa6fd92 commit 3b1ec89

File tree

5 files changed

+71
-46
lines changed

5 files changed

+71
-46
lines changed

Common/src/main/java/mezz/jei/common/platform/IPlatformItemStackHelper.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
public interface IPlatformItemStackHelper {
1818
int getBurnTime(ItemStack itemStack, RecipeType<?> recipeType, FuelValues fuelValues);
1919

20-
boolean isBookEnchantable(ItemStack stack, ItemStack book);
21-
2220
Optional<String> getCreatorModId(ItemStack stack);
2321

2422
List<Component> getTestTooltip(@Nullable Player player, ItemStack itemStack);

Fabric/src/main/java/mezz/jei/fabric/platform/ItemStackHelper.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ public int getBurnTime(ItemStack itemStack, RecipeType<?> recipeType, FuelValues
2626
return fuelValues.burnDuration(itemStack);
2727
}
2828

29-
@Override
30-
public boolean isBookEnchantable(ItemStack stack, ItemStack book) {
31-
return true;
32-
}
33-
3429
@Override
3530
public Optional<String> getCreatorModId(ItemStack stack) {
3631
return Optional.empty();

Library/src/main/java/mezz/jei/library/plugins/vanilla/anvil/AnvilRecipeMaker.java

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package mezz.jei.library.plugins.vanilla.anvil;
22

3-
import com.google.common.collect.Lists;
43
import mezz.jei.api.constants.VanillaTypes;
54
import mezz.jei.api.ingredients.IIngredientHelper;
65
import mezz.jei.api.recipe.vanilla.IJeiAnvilRecipe;
@@ -24,6 +23,7 @@
2423
import net.minecraft.world.entity.player.Inventory;
2524
import net.minecraft.world.entity.player.Player;
2625
import net.minecraft.world.inventory.AnvilMenu;
26+
import net.minecraft.world.inventory.Slot;
2727
import net.minecraft.world.item.Item;
2828
import net.minecraft.world.item.ItemStack;
2929
import net.minecraft.world.item.Items;
@@ -35,6 +35,8 @@
3535
import net.minecraft.world.item.enchantment.ItemEnchantments;
3636
import org.apache.logging.log4j.LogManager;
3737
import org.apache.logging.log4j.Logger;
38+
import org.jetbrains.annotations.Nullable;
39+
import org.jetbrains.annotations.Unmodifiable;
3840

3941
import java.util.List;
4042
import java.util.Objects;
@@ -44,6 +46,7 @@
4446
public final class AnvilRecipeMaker {
4547
private static final Logger LOGGER = LogManager.getLogger();
4648
private static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK);
49+
private static @Nullable AnvilMenu ANVIL_MENU = null;
4750

4851
private AnvilRecipeMaker() {
4952
}
@@ -59,20 +62,17 @@ public static List<IJeiAnvilRecipe> getAnvilRecipes(IVanillaRecipeFactory vanill
5962

6063
private static final class EnchantmentData {
6164
private final Holder<Enchantment> enchantment;
65+
@Unmodifiable
6266
private final List<ItemStack> enchantedBooks;
6367

6468
private EnchantmentData(Holder<Enchantment> enchantment) {
6569
this.enchantment = enchantment;
6670
this.enchantedBooks = getEnchantedBooks(enchantment);
6771
}
6872

69-
public List<ItemStack> getEnchantedBooks(ItemStack ingredient) {
70-
IPlatformItemStackHelper itemStackHelper = Services.PLATFORM.getItemStackHelper();
71-
var list = enchantedBooks.stream()
72-
.filter(enchantedBook -> itemStackHelper.isBookEnchantable(ingredient, enchantedBook))
73-
.toList();
74-
// avoid using copy of list if it contains the exact same items
75-
return list.size() == enchantedBooks.size() ? enchantedBooks : list;
73+
@Unmodifiable
74+
public List<ItemStack> getEnchantedBooks() {
75+
return enchantedBooks;
7676
}
7777

7878
private boolean canEnchant(IPlatformItemStackHelper itemStackHelper, ItemStack ingredient) {
@@ -120,25 +120,34 @@ private static Stream<IJeiAnvilRecipe> getBookEnchantmentRecipes(
120120
var ingredientSingletonList = List.of(ingredient);
121121
return enchantmentDatas.stream()
122122
.filter(data -> data.canEnchant(itemStackHelper, ingredient))
123-
.map(data -> data.getEnchantedBooks(ingredient))
124-
.filter(enchantedBooks -> !enchantedBooks.isEmpty())
125-
.map(enchantedBooks -> {
123+
.mapMulti((data, consumer) -> {
124+
List<ItemStack> enchantedBooks = data.getEnchantedBooks();
126125
List<ItemStack> outputs = getEnchantedIngredients(ingredient, enchantedBooks);
127-
// All lists given here are immutable, and we want to keep the transforming list from outputs,
128-
// so we call the AnvilRecipe constructor directly
129-
return new AnvilRecipe(ingredientSingletonList, enchantedBooks, outputs, null);
126+
if (outputs.isEmpty()) {
127+
return;
128+
}
129+
// All lists given here are immutable, so we call the AnvilRecipe constructor directly
130+
AnvilRecipe anvilRecipe = new AnvilRecipe(ingredientSingletonList, enchantedBooks, outputs, null);
131+
consumer.accept(anvilRecipe);
130132
});
131133
}
132134

133135
private static List<ItemStack> getEnchantedIngredients(ItemStack ingredient, List<ItemStack> enchantedBooks) {
134-
return Lists.transform(enchantedBooks, enchantedBook -> getEnchantedIngredient(ingredient, enchantedBook));
135-
}
136-
137-
private static ItemStack getEnchantedIngredient(ItemStack ingredient, ItemStack enchantedBook) {
138-
ItemStack enchantedIngredient = ingredient.copy();
139-
ItemEnchantments enchantments = EnchantmentHelper.getEnchantmentsForCrafting(enchantedBook);
140-
EnchantmentHelper.setEnchantments(enchantedIngredient, enchantments);
141-
return enchantedIngredient;
136+
AnvilMenu anvilMenu = getFakeAnvilMenu();
137+
if (anvilMenu == null) {
138+
return List.of();
139+
}
140+
return enchantedBooks.stream()
141+
.map(enchantedBook -> {
142+
AnvilMenu result = setAnvilMenu(anvilMenu, ingredient, enchantedBook);
143+
if (result == null) {
144+
return ItemStack.EMPTY;
145+
}
146+
Slot resultSlot = result.slots.get(result.getResultSlot());
147+
return resultSlot.getItem();
148+
})
149+
.filter(i -> !i.isEmpty())
150+
.toList();
142151
}
143152

144153
private static class RepairData {
@@ -312,21 +321,50 @@ private static Stream<IJeiAnvilRecipe> getRepairRecipes(
312321
}
313322

314323
public static int findLevelsCost(ItemStack leftStack, ItemStack rightStack) {
315-
Player player = Minecraft.getInstance().player;
316-
if (player == null) {
324+
AnvilMenu anvilMenu = getFakeAnvilMenu();
325+
if (anvilMenu == null) {
317326
return -1;
318327
}
319-
Inventory fakeInventory = new Inventory(player, new EntityEquipment());
328+
AnvilMenu result = setAnvilMenu(anvilMenu, leftStack, rightStack);
329+
if (result == null) {
330+
return -1;
331+
}
332+
return result.getCost();
333+
}
334+
335+
@Nullable
336+
private static AnvilMenu getFakeAnvilMenu() {
337+
if (ANVIL_MENU == null) {
338+
Player player = Minecraft.getInstance().player;
339+
if (player == null) {
340+
return null;
341+
}
342+
Inventory fakeInventory = new Inventory(player, new EntityEquipment());
343+
ANVIL_MENU = new AnvilMenu(0, fakeInventory);
344+
return ANVIL_MENU;
345+
}
346+
return ANVIL_MENU;
347+
}
348+
349+
@Nullable
350+
private static AnvilMenu setAnvilMenu(AnvilMenu anvilMenu, ItemStack leftStack, ItemStack rightStack) {
320351
try {
321-
AnvilMenu repair = new AnvilMenu(0, fakeInventory);
322-
repair.slots.get(0).set(leftStack);
323-
repair.slots.get(1).set(rightStack);
324-
return repair.getCost();
352+
Slot leftSlot = anvilMenu.slots.get(0);
353+
Slot rightSlot = anvilMenu.slots.get(1);
354+
355+
// setting the stack triggers a recalculation of the recipe, so avoid it when possible
356+
if (leftSlot.getItem() != leftStack) {
357+
leftSlot.set(leftStack);
358+
}
359+
if (rightSlot.getItem() != rightStack) {
360+
rightSlot.set(rightStack);
361+
}
362+
return anvilMenu;
325363
} catch (RuntimeException e) {
326364
String left = ErrorUtil.getItemStackInfo(leftStack);
327365
String right = ErrorUtil.getItemStackInfo(rightStack);
328-
LOGGER.error("Could not get anvil level cost for: ({} and {}).", left, right, e);
329-
return -1;
366+
LOGGER.error("Could not set anvil recipe for: ({} and {}).", left, right, e);
367+
return null;
330368
}
331369
}
332370
}

NeoForge/src/main/java/mezz/jei/neoforge/platform/ItemStackHelper.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,6 @@ public int getBurnTime(ItemStack itemStack, RecipeType<?> recipeType, FuelValues
3939
}
4040
}
4141

42-
@Override
43-
public boolean isBookEnchantable(ItemStack stack, ItemStack book) {
44-
Item item = stack.getItem();
45-
return item.isBookEnchantable(stack, book);
46-
}
47-
4842
@Override
4943
public Optional<String> getCreatorModId(ItemStack stack) {
5044
Minecraft minecraft = Minecraft.getInstance();

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ minecraftVersionRangeStart=1.21.5
2727

2828
# Neoforge
2929
# https://projects.neoforged.net/neoforged/neoforge
30-
neoforgeVersion=21.5.67-beta
30+
neoforgeVersion=21.5.75
3131
neoforgeLoaderVersionRange=[4,)
32-
neoforgeVersionRange=[21.5,)
32+
neoforgeVersionRange=[21.5.74,)
3333
neogradle.subsystems.conventions.runs.create-default-run-per-type=false
3434

3535
# Fabric

0 commit comments

Comments
 (0)