RecipeFinderUtil改用JEI提供的API
This commit is contained in:
parent
c517d799a8
commit
ce09d26fe1
|
|
@ -1,28 +1,24 @@
|
||||||
package com.extendedae_plus.client.event;
|
package com.extendedae_plus.client.event;
|
||||||
|
|
||||||
import appeng.api.stacks.AEItemKey;
|
|
||||||
import appeng.api.stacks.AEKey;
|
|
||||||
import com.extendedae_plus.ExtendedAEPlus;
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
import com.extendedae_plus.client.ModKeybindings;
|
import com.extendedae_plus.client.ModKeybindings;
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
||||||
import com.extendedae_plus.network.pattern.CreateCtrlQPatternC2SPacket;
|
import com.extendedae_plus.network.pattern.CreateCtrlQPatternC2SPacket;
|
||||||
import com.extendedae_plus.util.RecipeFinderUtil;
|
import com.extendedae_plus.util.RecipeFinderUtil;
|
||||||
|
import com.extendedae_plus.util.RecipeInfo;
|
||||||
import mezz.jei.api.constants.VanillaTypes;
|
import mezz.jei.api.constants.VanillaTypes;
|
||||||
import mezz.jei.api.ingredients.ITypedIngredient;
|
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.CraftingRecipe;
|
|
||||||
import net.minecraft.world.item.crafting.Ingredient;
|
|
||||||
import net.minecraft.world.item.crafting.Recipe;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.client.event.ScreenEvent;
|
import net.minecraftforge.client.event.ScreenEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -68,11 +64,10 @@ public class CtrlQPatternKeyHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找相关配方
|
// 查找相关配方(使用新的 API,包含完整数量信息)
|
||||||
Minecraft mc = Minecraft.getInstance();
|
Minecraft mc = Minecraft.getInstance();
|
||||||
List<Recipe<?>> recipes = RecipeFinderUtil.findRecipesByIngredient(
|
List<RecipeInfo> recipes = RecipeFinderUtil.findRecipesByIngredient(
|
||||||
ingredient.get(),
|
ingredient.get()
|
||||||
mc.level
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (recipes.isEmpty()) {
|
if (recipes.isEmpty()) {
|
||||||
|
|
@ -85,21 +80,19 @@ public class CtrlQPatternKeyHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动选择最佳配方(优先CraftingRecipe)
|
// 自动选择最佳配方(优先工作台配方)
|
||||||
Recipe<?> selectedRecipe = RecipeFinderUtil.selectBestRecipe(recipes);
|
RecipeInfo selectedRecipeInfo = RecipeFinderUtil.selectBestRecipe(recipes);
|
||||||
if (selectedRecipe == null) {
|
if (selectedRecipeInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isCraftingPattern = selectedRecipe instanceof CraftingRecipe;
|
|
||||||
|
|
||||||
// 应用JEI书签优先级选择材料
|
// 应用JEI书签优先级选择材料
|
||||||
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(selectedRecipe);
|
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(selectedRecipeInfo);
|
||||||
|
|
||||||
// 发送网络包到服务器
|
// 发送网络包到服务器
|
||||||
ModNetwork.CHANNEL.sendToServer(new CreateCtrlQPatternC2SPacket(
|
ModNetwork.CHANNEL.sendToServer(new CreateCtrlQPatternC2SPacket(
|
||||||
selectedRecipe.getId(),
|
selectedRecipeInfo.getRecipe().getId(),
|
||||||
isCraftingPattern,
|
selectedRecipeInfo.isCraftingRecipe(),
|
||||||
selectedIngredients
|
selectedIngredients
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -110,63 +103,26 @@ public class CtrlQPatternKeyHandler {
|
||||||
/**
|
/**
|
||||||
* 应用JEI书签优先级选择配方材料
|
* 应用JEI书签优先级选择配方材料
|
||||||
*
|
*
|
||||||
* <p>对配方的每个 Ingredient,选择 JEI 书签中优先级最高的物品</p>
|
* <p>对配方的每个输入槽位,选择 JEI 书签中优先级最高的物品</p>
|
||||||
* <p>如果没有在书签中,则使用配方默认的第一个物品</p>
|
* <p>如果没有在书签中,则使用槽位的第一个物品</p>
|
||||||
*
|
*
|
||||||
* @param recipe 配方
|
* @param recipeInfo 配方信息(包含完整的输入输出数量)
|
||||||
* @return 选择的材料列表
|
* @return 选择的材料列表
|
||||||
*/
|
*/
|
||||||
private static List<ItemStack> selectIngredientsWithJeiPriority(Recipe<?> recipe) {
|
private static List<ItemStack> selectIngredientsWithJeiPriority(RecipeInfo recipeInfo) {
|
||||||
// 获取JEI书签列表并构建优先级映射
|
// 获取JEI书签列表并构建优先级映射
|
||||||
List<? extends ITypedIngredient<?>> bookmarks = JeiRuntimeProxy.getBookmarkList();
|
List<? extends ITypedIngredient<?>> bookmarks = JeiRuntimeProxy.getBookmarkList();
|
||||||
Map<AEKey, Integer> priorities = new HashMap<>();
|
Map<Item, Integer> priorities = new HashMap<>();
|
||||||
AtomicInteger index = new AtomicInteger(Integer.MAX_VALUE);
|
AtomicInteger index = new AtomicInteger(Integer.MAX_VALUE);
|
||||||
|
|
||||||
// 构建优先级映射 (数值越小 = 优先级越高,与EncodingHelperMixin逻辑一致)
|
// 构建优先级映射 (数值越小 = 优先级越高)
|
||||||
for (ITypedIngredient<?> ingredient : bookmarks) {
|
for (ITypedIngredient<?> ingredient : bookmarks) {
|
||||||
ingredient.getIngredient(VanillaTypes.ITEM_STACK).ifPresent(itemStack ->
|
ingredient.getIngredient(VanillaTypes.ITEM_STACK).ifPresent(itemStack ->
|
||||||
priorities.put(AEItemKey.of(itemStack), index.getAndDecrement())
|
priorities.put(itemStack.getItem(), index.getAndDecrement())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ItemStack> selected = new ArrayList<>();
|
// 使用 RecipeInfo 的方法选择最佳输入
|
||||||
|
return recipeInfo.selectBestInputs(priorities);
|
||||||
// 对每个 ingredient 选择优先级最高的物品
|
|
||||||
for (Ingredient ingredient : recipe.getIngredients()) {
|
|
||||||
if (ingredient.isEmpty()) {
|
|
||||||
selected.add(ItemStack.EMPTY);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemStack[] items = ingredient.getItems();
|
|
||||||
if (items.length == 0) {
|
|
||||||
selected.add(ItemStack.EMPTY);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 选择优先级最高的 (如果都不在书签中,选第一个)
|
|
||||||
ItemStack best = items[0];
|
|
||||||
int bestPriority = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
// 检查第一个物品的优先级
|
|
||||||
AEKey firstKey = AEItemKey.of(best);
|
|
||||||
if (priorities.containsKey(firstKey)) {
|
|
||||||
bestPriority = priorities.get(firstKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历其他选项
|
|
||||||
for (int i = 1; i < items.length; i++) {
|
|
||||||
AEKey key = AEItemKey.of(items[i]);
|
|
||||||
int priority = priorities.getOrDefault(key, Integer.MAX_VALUE);
|
|
||||||
if (priority < bestPriority) {
|
|
||||||
bestPriority = priority;
|
|
||||||
best = items[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selected.add(best.copy());
|
|
||||||
}
|
|
||||||
|
|
||||||
return selected;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,130 +1,210 @@
|
||||||
package com.extendedae_plus.util;
|
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.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.ingredients.ITypedIngredient;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
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.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.CraftingRecipe;
|
import net.minecraft.world.item.crafting.CraftingRecipe;
|
||||||
import net.minecraft.world.item.crafting.Recipe;
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配方查找工具类
|
* 配方查找工具类
|
||||||
*
|
*
|
||||||
* <p>根据物品查找相关配方,优先返回工作台配方(CraftingRecipe)</p>
|
* <p>使用 JEI API 根据物品查找相关配方,返回包含完整数量信息的 RecipeInfo</p>
|
||||||
*/
|
*/
|
||||||
public class RecipeFinderUtil {
|
public class RecipeFinderUtil {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAE Plus - RecipeFinder");
|
private static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAE Plus - RecipeFinder");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据JEI物品查找相关配方
|
* 根据JEI物品查找相关配方(仅搜索以该物品为输出的配方)
|
||||||
*
|
*
|
||||||
* @param ingredient JEI物品
|
* @param ingredient JEI物品
|
||||||
* @param level 当前世界
|
* @return 相关配方信息列表(包含完整的输入输出数量)
|
||||||
* @return 相关配方列表
|
|
||||||
*/
|
*/
|
||||||
public static List<Recipe<?>> findRecipesByIngredient(ITypedIngredient<?> ingredient, Level level) {
|
public static List<RecipeInfo> findRecipesByIngredient(ITypedIngredient<?> ingredient) {
|
||||||
if (ingredient.getType() == VanillaTypes.ITEM_STACK) {
|
// 获取 JEI Runtime
|
||||||
ItemStack stack = (ItemStack) ingredient.getIngredient();
|
IJeiRuntime jeiRuntime = JeiRuntimeProxy.get();
|
||||||
return findRecipesByItem(stack, level);
|
if (jeiRuntime == null) {
|
||||||
|
LOGGER.warn("[RecipeFinder] JEI Runtime not available");
|
||||||
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.warn("[RecipeFinder] Unsupported ingredient type: {}", ingredient.getType());
|
// 只支持物品类型
|
||||||
// TODO: Support fluids, chemicals, and other AE2-compatible types
|
if (ingredient.getType() != VanillaTypes.ITEM_STACK) {
|
||||||
return List.of();
|
LOGGER.warn("[RecipeFinder] Unsupported ingredient type: {}", ingredient.getType());
|
||||||
}
|
// TODO: Support fluids, chemicals, and other AE2-compatible types
|
||||||
|
return List.of();
|
||||||
/**
|
|
||||||
* 根据物品查找相关配方
|
|
||||||
*
|
|
||||||
* @param item 目标物品
|
|
||||||
* @param level 当前世界
|
|
||||||
* @return 配方列表
|
|
||||||
*/
|
|
||||||
private static List<Recipe<?>> findRecipesByItem(ItemStack item, Level level) {
|
|
||||||
List<Recipe<?>> results = new ArrayList<>();
|
|
||||||
|
|
||||||
// 1. 查找以该物品为输出的配方
|
|
||||||
for (Recipe<?> recipe : level.getRecipeManager().getRecipes()) {
|
|
||||||
if (matchesOutput(recipe, item, level)) {
|
|
||||||
results.add(recipe);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 如果按住Shift,也查找以该物品为输入的配方
|
IJeiHelpers jeiHelpers = jeiRuntime.getJeiHelpers();
|
||||||
if (Screen.hasShiftDown()) {
|
IRecipeManager recipeManager = jeiRuntime.getRecipeManager();
|
||||||
for (Recipe<?> recipe : level.getRecipeManager().getRecipes()) {
|
IFocusFactory focusFactory = jeiHelpers.getFocusFactory();
|
||||||
if (matchesInput(recipe, item) && !results.contains(recipe)) {
|
|
||||||
results.add(recipe);
|
// 创建输出焦点(OUTPUT role)
|
||||||
|
IFocus<?> outputFocus = focusFactory.createFocus(
|
||||||
|
RecipeIngredientRole.OUTPUT,
|
||||||
|
ingredient
|
||||||
|
);
|
||||||
|
|
||||||
|
List<RecipeInfo> results = new ArrayList<>();
|
||||||
|
|
||||||
|
// 查找工作台配方
|
||||||
|
try {
|
||||||
|
IRecipeCategory<CraftingRecipe> craftingCategory = recipeManager.getRecipeCategory(RecipeTypes.CRAFTING);
|
||||||
|
|
||||||
|
recipeManager.createRecipeLookup(RecipeTypes.CRAFTING)
|
||||||
|
.limitFocus(List.of(outputFocus))
|
||||||
|
.get()
|
||||||
|
.forEach(recipe -> {
|
||||||
|
// 创建配方布局以获取完整信息
|
||||||
|
Optional<IRecipeLayoutDrawable<CraftingRecipe>> 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<Recipe<?>> category = (IRecipeCategory<Recipe<?>>) recipeManager.getRecipeCategory(recipeType);
|
||||||
|
|
||||||
|
recipeManager.createRecipeLookup(recipeType)
|
||||||
|
.limitFocus(List.of(outputFocus))
|
||||||
|
.get()
|
||||||
|
.forEach(recipe -> {
|
||||||
|
if (recipe instanceof Recipe<?> rawRecipe) {
|
||||||
|
// 创建配方布局以获取完整信息
|
||||||
|
Optional<IRecipeLayoutDrawable<Recipe<?>>> 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 优先级排序: CraftingRecipe优先
|
LOGGER.debug("[RecipeFinder] Found {} recipes for output: {}",
|
||||||
results.sort((r1, r2) -> {
|
results.size(),
|
||||||
boolean isCrafting1 = r1 instanceof CraftingRecipe;
|
((ItemStack) ingredient.getIngredient()).getDescriptionId());
|
||||||
boolean isCrafting2 = r2 instanceof CraftingRecipe;
|
|
||||||
if (isCrafting1 && !isCrafting2) return -1; // r1优先
|
|
||||||
if (!isCrafting1 && isCrafting2) return 1; // r2优先
|
|
||||||
return 0; // 保持原顺序
|
|
||||||
});
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从配方布局中提取完整的配方信息
|
||||||
|
*
|
||||||
|
* @param recipe 原始配方对象
|
||||||
|
* @param layout JEI 配方布局(包含完整的槽位和数量信息)
|
||||||
|
* @param isCrafting 是否为工作台配方
|
||||||
|
* @return 配方信息,如果提取失败返回 null
|
||||||
|
*/
|
||||||
|
private static <T> RecipeInfo extractRecipeInfo(
|
||||||
|
Recipe<?> recipe,
|
||||||
|
IRecipeLayoutDrawable<T> layout,
|
||||||
|
boolean isCrafting
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
IRecipeSlotsView slotsView = layout.getRecipeSlotsView();
|
||||||
|
|
||||||
|
// 提取输入槽位
|
||||||
|
List<IRecipeSlotView> inputSlots = slotsView.getSlotViews(RecipeIngredientRole.INPUT);
|
||||||
|
List<List<ItemStack>> inputs = new ArrayList<>();
|
||||||
|
|
||||||
|
for (IRecipeSlotView slot : inputSlots) {
|
||||||
|
List<ItemStack> slotItems = slot.getItemStacks()
|
||||||
|
.map(ItemStack::copy) // 复制以保留数量信息
|
||||||
|
.toList();
|
||||||
|
inputs.add(slotItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取输出槽位
|
||||||
|
List<IRecipeSlotView> outputSlots = slotsView.getSlotViews(RecipeIngredientRole.OUTPUT);
|
||||||
|
List<ItemStack> 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 配方列表
|
* @param recipes 配方信息列表
|
||||||
* @return 最佳配方,如果列表为空返回null
|
* @return 最佳配方信息,如果列表为空返回null
|
||||||
*/
|
*/
|
||||||
public static Recipe<?> selectBestRecipe(List<Recipe<?>> recipes) {
|
public static RecipeInfo selectBestRecipe(List<RecipeInfo> recipes) {
|
||||||
if (recipes.isEmpty()) {
|
if (recipes.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先返回CraftingRecipe
|
// 优先返回工作台配方
|
||||||
for (Recipe<?> recipe : recipes) {
|
for (RecipeInfo info : recipes) {
|
||||||
if (recipe instanceof CraftingRecipe) {
|
if (info.isCraftingRecipe()) {
|
||||||
return recipe;
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 没有工作台配方,返回第一个
|
// 没有工作台配方,返回第一个
|
||||||
return recipes.get(0);
|
return recipes.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查配方输出是否匹配目标物品
|
|
||||||
*/
|
|
||||||
private static boolean matchesOutput(Recipe<?> recipe, ItemStack target, Level level) {
|
|
||||||
try {
|
|
||||||
ItemStack result = recipe.getResultItem(level.registryAccess());
|
|
||||||
if (result.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ItemStack.isSameItemSameTags(result, target);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.warn("[RecipeFinder] Exception in matchesOutput for recipe {}: {}", recipe.getId(), e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查配方输入是否包含目标物品
|
|
||||||
*/
|
|
||||||
private static boolean matchesInput(Recipe<?> recipe, ItemStack target) {
|
|
||||||
try {
|
|
||||||
return recipe.getIngredients().stream()
|
|
||||||
.anyMatch(ingredient -> ingredient.test(target));
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.warn("[RecipeFinder] Exception in matchesInput for recipe {}: {}", recipe.getId(), e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
96
src/main/java/com/extendedae_plus/util/RecipeInfo.java
Normal file
96
src/main/java/com/extendedae_plus/util/RecipeInfo.java
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.extendedae_plus.util;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配方完整信息
|
||||||
|
*
|
||||||
|
* <p>包含配方的所有输入材料(带数量)和输出物品</p>
|
||||||
|
*/
|
||||||
|
public class RecipeInfo {
|
||||||
|
private final Recipe<?> recipe;
|
||||||
|
private final boolean isCraftingRecipe;
|
||||||
|
private final List<List<ItemStack>> inputs; // 每个槽位的所有可能物品(包含数量)
|
||||||
|
private final List<ItemStack> outputs; // 输出物品(包含数量)
|
||||||
|
|
||||||
|
public RecipeInfo(
|
||||||
|
Recipe<?> recipe,
|
||||||
|
boolean isCraftingRecipe,
|
||||||
|
List<List<ItemStack>> inputs,
|
||||||
|
List<ItemStack> outputs
|
||||||
|
) {
|
||||||
|
this.recipe = recipe;
|
||||||
|
this.isCraftingRecipe = isCraftingRecipe;
|
||||||
|
this.inputs = inputs;
|
||||||
|
this.outputs = outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取原始配方对象
|
||||||
|
*/
|
||||||
|
public Recipe<?> getRecipe() {
|
||||||
|
return recipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为工作台配方
|
||||||
|
*/
|
||||||
|
public boolean isCraftingRecipe() {
|
||||||
|
return isCraftingRecipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取输入材料列表
|
||||||
|
*
|
||||||
|
* @return 每个槽位的所有可能物品列表(包含数量)
|
||||||
|
*/
|
||||||
|
public List<List<ItemStack>> getInputs() {
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取输出物品列表
|
||||||
|
*
|
||||||
|
* @return 输出物品列表(包含数量)
|
||||||
|
*/
|
||||||
|
public List<ItemStack> getOutputs() {
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用 JEI 书签优先级选择最佳输入材料
|
||||||
|
*
|
||||||
|
* @param bookmarkPriorities 书签优先级映射(物品 -> 优先级,数值越小优先级越高)
|
||||||
|
* @return 选择的材料列表(每个槽位一个物品)
|
||||||
|
*/
|
||||||
|
public List<ItemStack> selectBestInputs(java.util.Map<net.minecraft.world.item.Item, Integer> bookmarkPriorities) {
|
||||||
|
java.util.List<ItemStack> selected = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
for (List<ItemStack> slotOptions : inputs) {
|
||||||
|
if (slotOptions.isEmpty()) {
|
||||||
|
selected.add(ItemStack.EMPTY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择优先级最高的物品(如果都不在书签中,选第一个)
|
||||||
|
ItemStack best = slotOptions.get(0);
|
||||||
|
int bestPriority = bookmarkPriorities.getOrDefault(best.getItem(), Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
for (int i = 1; i < slotOptions.size(); i++) {
|
||||||
|
ItemStack option = slotOptions.get(i);
|
||||||
|
int priority = bookmarkPriorities.getOrDefault(option.getItem(), Integer.MAX_VALUE);
|
||||||
|
if (priority < bestPriority) {
|
||||||
|
bestPriority = priority;
|
||||||
|
best = option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selected.add(best.copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user