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);
}
}