package com.extendedae_plus.util; import com.extendedae_plus.integration.jei.JeiRuntimeProxy; import mezz.jei.api.constants.RecipeTypes; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.IRecipeLayoutDrawable; import mezz.jei.api.gui.ingredient.IRecipeSlotView; import mezz.jei.api.gui.ingredient.IRecipeSlotsView; import mezz.jei.api.helpers.IJeiHelpers; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.recipe.IFocus; import mezz.jei.api.recipe.IFocusFactory; import mezz.jei.api.recipe.IRecipeManager; import mezz.jei.api.recipe.RecipeIngredientRole; import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IJeiRuntime; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingRecipe; import net.minecraft.world.item.crafting.Recipe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * 配方查找工具类 * *

使用 JEI API 根据物品查找相关配方,返回包含完整数量信息的 RecipeInfo

*/ public class RecipeFinderUtil { private static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAE Plus - RecipeFinder"); /** * 根据JEI物品查找相关配方(仅搜索以该物品为输出的配方) * * @param ingredient JEI物品 * @return 相关配方信息列表(包含完整的输入输出数量) */ public static List findRecipesByIngredient(ITypedIngredient ingredient) { // 获取 JEI Runtime IJeiRuntime jeiRuntime = JeiRuntimeProxy.get(); if (jeiRuntime == null) { LOGGER.warn("[RecipeFinder] JEI Runtime not available"); return List.of(); } // 只支持物品类型 if (ingredient.getType() != VanillaTypes.ITEM_STACK) { LOGGER.warn("[RecipeFinder] Unsupported ingredient type: {}", ingredient.getType()); // TODO: Support fluids, chemicals, and other AE2-compatible types return List.of(); } IJeiHelpers jeiHelpers = jeiRuntime.getJeiHelpers(); IRecipeManager recipeManager = jeiRuntime.getRecipeManager(); IFocusFactory focusFactory = jeiHelpers.getFocusFactory(); // 创建输出焦点(OUTPUT role) IFocus outputFocus = focusFactory.createFocus( RecipeIngredientRole.OUTPUT, ingredient ); List results = new ArrayList<>(); // 查找工作台配方 try { IRecipeCategory craftingCategory = recipeManager.getRecipeCategory(RecipeTypes.CRAFTING); recipeManager.createRecipeLookup(RecipeTypes.CRAFTING) .limitFocus(List.of(outputFocus)) .get() .forEach(recipe -> { // 创建配方布局以获取完整信息 Optional> layoutOpt = recipeManager.createRecipeLayoutDrawable( craftingCategory, recipe, focusFactory.getEmptyFocusGroup() ); layoutOpt.ifPresent(layout -> { RecipeInfo info = extractRecipeInfo(recipe, layout, true); if (info != null) { results.add(info); } }); }); } catch (Exception e) { LOGGER.warn("[RecipeFinder] Error searching crafting recipes: {}", e.getMessage()); } // 查找其他所有配方类型(排除工作台配方) try { jeiHelpers.getAllRecipeTypes().forEach(recipeType -> { // 跳过工作台配方(已经处理过) if (recipeType.equals(RecipeTypes.CRAFTING)) { return; } try { @SuppressWarnings("unchecked") IRecipeCategory> category = (IRecipeCategory>) recipeManager.getRecipeCategory(recipeType); recipeManager.createRecipeLookup(recipeType) .limitFocus(List.of(outputFocus)) .get() .forEach(recipe -> { if (recipe instanceof Recipe rawRecipe) { // 创建配方布局以获取完整信息 Optional>> layoutOpt = recipeManager.createRecipeLayoutDrawable( category, rawRecipe, focusFactory.getEmptyFocusGroup() ); layoutOpt.ifPresent(layout -> { RecipeInfo info = extractRecipeInfo(rawRecipe, layout, false); if (info != null) { results.add(info); } }); } }); } catch (Exception e) { // 某些配方类型可能不支持,静默忽略 } }); } catch (Exception e) { LOGGER.warn("[RecipeFinder] Error searching other recipe types: {}", e.getMessage()); } LOGGER.debug("[RecipeFinder] Found {} recipes for output: {}", results.size(), ((ItemStack) ingredient.getIngredient()).getDescriptionId()); return results; } /** * 从配方布局中提取完整的配方信息 * * @param recipe 原始配方对象 * @param layout JEI 配方布局(包含完整的槽位和数量信息) * @param isCrafting 是否为工作台配方 * @return 配方信息,如果提取失败返回 null */ private static RecipeInfo extractRecipeInfo( Recipe recipe, IRecipeLayoutDrawable layout, boolean isCrafting ) { try { IRecipeSlotsView slotsView = layout.getRecipeSlotsView(); // 提取输入槽位 List inputSlots = slotsView.getSlotViews(RecipeIngredientRole.INPUT); List> inputs = new ArrayList<>(); for (IRecipeSlotView slot : inputSlots) { List slotItems = slot.getItemStacks() .map(ItemStack::copy) // 复制以保留数量信息 .toList(); inputs.add(slotItems); } // 提取输出槽位 List outputSlots = slotsView.getSlotViews(RecipeIngredientRole.OUTPUT); List outputs = new ArrayList<>(); for (IRecipeSlotView slot : outputSlots) { slot.getItemStacks() .map(ItemStack::copy) // 复制以保留数量信息 .forEach(outputs::add); } return new RecipeInfo(recipe, isCrafting, inputs, outputs); } catch (Exception e) { LOGGER.warn("[RecipeFinder] Failed to extract recipe info for {}: {}", recipe.getId(), e.getMessage()); return null; } } /** * 选择最佳配方(优先选择工作台配方) * * @param recipes 配方信息列表 * @return 最佳配方信息,如果列表为空返回null */ public static RecipeInfo selectBestRecipe(List recipes) { if (recipes.isEmpty()) { return null; } // 优先返回工作台配方 for (RecipeInfo info : recipes) { if (info.isCraftingRecipe()) { return info; } } // 没有工作台配方,返回第一个 return recipes.get(0); } }