适配流体配方
This commit is contained in:
parent
ce09d26fe1
commit
9b8d712c94
|
|
@ -131,6 +131,9 @@ dependencies {
|
||||||
modCompileOnly "curse.maven:ftb-library-forge-404465:6807424"
|
modCompileOnly "curse.maven:ftb-library-forge-404465:6807424"
|
||||||
modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786"
|
modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786"
|
||||||
modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424"
|
modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424"
|
||||||
|
|
||||||
|
//MEK
|
||||||
|
modImplementation "curse.maven:mekanism-268560:6552911"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileJava {
|
compileJava {
|
||||||
|
|
|
||||||
|
|
@ -89,11 +89,15 @@ public class CtrlQPatternKeyHandler {
|
||||||
// 应用JEI书签优先级选择材料
|
// 应用JEI书签优先级选择材料
|
||||||
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(selectedRecipeInfo);
|
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(selectedRecipeInfo);
|
||||||
|
|
||||||
|
// 获取输出材料(转换为 ItemStack,流体会被包装)
|
||||||
|
List<ItemStack> selectedOutputs = convertOutputsToItemStacks(selectedRecipeInfo);
|
||||||
|
|
||||||
// 发送网络包到服务器
|
// 发送网络包到服务器
|
||||||
ModNetwork.CHANNEL.sendToServer(new CreateCtrlQPatternC2SPacket(
|
ModNetwork.CHANNEL.sendToServer(new CreateCtrlQPatternC2SPacket(
|
||||||
selectedRecipeInfo.getRecipe().getId(),
|
selectedRecipeInfo.getRecipe().getId(),
|
||||||
selectedRecipeInfo.isCraftingRecipe(),
|
selectedRecipeInfo.isCraftingRecipe(),
|
||||||
selectedIngredients
|
selectedIngredients,
|
||||||
|
selectedOutputs
|
||||||
));
|
));
|
||||||
|
|
||||||
// 消耗事件,防止传播
|
// 消耗事件,防止传播
|
||||||
|
|
@ -125,4 +129,25 @@ public class CtrlQPatternKeyHandler {
|
||||||
// 使用 RecipeInfo 的方法选择最佳输入
|
// 使用 RecipeInfo 的方法选择最佳输入
|
||||||
return recipeInfo.selectBestInputs(priorities);
|
return recipeInfo.selectBestInputs(priorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将配方输出转换为 ItemStack 列表(用于网络传输)
|
||||||
|
*
|
||||||
|
* <p>物品直接转换,流体会被包装为 GenericStack.wrapInItemStack</p>
|
||||||
|
*
|
||||||
|
* @param recipeInfo 配方信息
|
||||||
|
* @return ItemStack 列表(流体已包装)
|
||||||
|
*/
|
||||||
|
private static List<ItemStack> convertOutputsToItemStacks(RecipeInfo recipeInfo) {
|
||||||
|
return recipeInfo.getOutputs().stream()
|
||||||
|
.map(genericStack -> {
|
||||||
|
if (genericStack.what() instanceof appeng.api.stacks.AEItemKey itemKey) {
|
||||||
|
return itemKey.toStack((int) genericStack.amount());
|
||||||
|
} else {
|
||||||
|
// 流体或其他类型,使用包装
|
||||||
|
return appeng.api.stacks.GenericStack.wrapInItemStack(genericStack);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,18 +35,20 @@ import net.minecraftforge.network.NetworkEvent;
|
||||||
* C2S: Ctrl+Q快速创建样板数据包
|
* C2S: Ctrl+Q快速创建样板数据包
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 从客户端发送配方ID和选择的材料到服务器,服务器消耗空白样板并创建编码样板掉落到玩家脚下</p>
|
* 从客户端发送配方ID、选择的材料和输出到服务器,服务器消耗空白样板并创建编码样板掉落到玩家脚下</p>
|
||||||
*/
|
*/
|
||||||
public class CreateCtrlQPatternC2SPacket {
|
public class CreateCtrlQPatternC2SPacket {
|
||||||
|
|
||||||
private final ResourceLocation recipeId;
|
private final ResourceLocation recipeId;
|
||||||
private final boolean isCraftingPattern;
|
private final boolean isCraftingPattern;
|
||||||
private final List<ItemStack> selectedIngredients;
|
private final List<ItemStack> selectedIngredients;
|
||||||
|
private final List<ItemStack> outputs; // 输出材料(物品或包装的流体)
|
||||||
|
|
||||||
public CreateCtrlQPatternC2SPacket(ResourceLocation recipeId, boolean isCraftingPattern, List<ItemStack> selectedIngredients) {
|
public CreateCtrlQPatternC2SPacket(ResourceLocation recipeId, boolean isCraftingPattern, List<ItemStack> selectedIngredients, List<ItemStack> outputs) {
|
||||||
this.recipeId = recipeId;
|
this.recipeId = recipeId;
|
||||||
this.isCraftingPattern = isCraftingPattern;
|
this.isCraftingPattern = isCraftingPattern;
|
||||||
this.selectedIngredients = selectedIngredients;
|
this.selectedIngredients = selectedIngredients;
|
||||||
|
this.outputs = outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void encode(CreateCtrlQPatternC2SPacket msg, FriendlyByteBuf buf) {
|
public static void encode(CreateCtrlQPatternC2SPacket msg, FriendlyByteBuf buf) {
|
||||||
|
|
@ -56,17 +58,26 @@ public class CreateCtrlQPatternC2SPacket {
|
||||||
for (ItemStack stack : msg.selectedIngredients) {
|
for (ItemStack stack : msg.selectedIngredients) {
|
||||||
buf.writeItem(stack);
|
buf.writeItem(stack);
|
||||||
}
|
}
|
||||||
|
buf.writeInt(msg.outputs.size());
|
||||||
|
for (ItemStack stack : msg.outputs) {
|
||||||
|
buf.writeItem(stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CreateCtrlQPatternC2SPacket decode(FriendlyByteBuf buf) {
|
public static CreateCtrlQPatternC2SPacket decode(FriendlyByteBuf buf) {
|
||||||
ResourceLocation recipeId = buf.readResourceLocation();
|
ResourceLocation recipeId = buf.readResourceLocation();
|
||||||
boolean isCraftingPattern = buf.readBoolean();
|
boolean isCraftingPattern = buf.readBoolean();
|
||||||
int count = buf.readInt();
|
int ingredientCount = buf.readInt();
|
||||||
List<ItemStack> ingredients = new ArrayList<>();
|
List<ItemStack> ingredients = new ArrayList<>();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < ingredientCount; i++) {
|
||||||
ingredients.add(buf.readItem());
|
ingredients.add(buf.readItem());
|
||||||
}
|
}
|
||||||
return new CreateCtrlQPatternC2SPacket(recipeId, isCraftingPattern, ingredients);
|
int outputCount = buf.readInt();
|
||||||
|
List<ItemStack> outputs = new ArrayList<>();
|
||||||
|
for (int i = 0; i < outputCount; i++) {
|
||||||
|
outputs.add(buf.readItem());
|
||||||
|
}
|
||||||
|
return new CreateCtrlQPatternC2SPacket(recipeId, isCraftingPattern, ingredients, outputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void handle(CreateCtrlQPatternC2SPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
public static void handle(CreateCtrlQPatternC2SPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||||
|
|
@ -101,7 +112,7 @@ public class CreateCtrlQPatternC2SPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 创建样板
|
// 3. 创建样板
|
||||||
ItemStack pattern = createPattern(recipe, msg.isCraftingPattern, msg.selectedIngredients, player);
|
ItemStack pattern = createPattern(recipe, msg.isCraftingPattern, msg.selectedIngredients, msg.outputs, player);
|
||||||
|
|
||||||
if (pattern.isEmpty()) {
|
if (pattern.isEmpty()) {
|
||||||
// 创建失败,退还空白样板
|
// 创建失败,退还空白样板
|
||||||
|
|
@ -247,15 +258,16 @@ public class CreateCtrlQPatternC2SPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从配方创建样板
|
* 从配方创建样板(支持物品和流体)
|
||||||
*
|
*
|
||||||
* @param recipe 配方
|
* @param recipe 配方
|
||||||
* @param isCrafting 是否为合成样板
|
* @param isCrafting 是否为合成样板
|
||||||
* @param selectedIngredients 客户端选择的材料(应用JEI优先级后)
|
* @param selectedIngredients 客户端选择的材料(应用JEI优先级后,流体已包装为 GenericStack.wrapInItemStack)
|
||||||
|
* @param selectedOutputs 客户端传递的输出材料(物品或包装的流体)
|
||||||
* @param player 玩家
|
* @param player 玩家
|
||||||
* @return 编码的样板物品
|
* @return 编码的样板物品
|
||||||
*/
|
*/
|
||||||
private static ItemStack createPattern(Recipe<?> recipe, boolean isCrafting, List<ItemStack> selectedIngredients, ServerPlayer player) {
|
private static ItemStack createPattern(Recipe<?> recipe, boolean isCrafting, List<ItemStack> selectedIngredients, List<ItemStack> selectedOutputs, ServerPlayer player) {
|
||||||
try {
|
try {
|
||||||
if (isCrafting && recipe instanceof CraftingRecipe craftingRecipe) {
|
if (isCrafting && recipe instanceof CraftingRecipe craftingRecipe) {
|
||||||
// ===== 合成样板创建路径 =====
|
// ===== 合成样板创建路径 =====
|
||||||
|
|
@ -289,28 +301,45 @@ public class CreateCtrlQPatternC2SPacket {
|
||||||
return encodedPattern;
|
return encodedPattern;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// ===== 处理样板创建路径 =====
|
// ===== 处理样板创建路径(支持物品和流体)=====
|
||||||
|
|
||||||
List<GenericStack> inputs = new ArrayList<>();
|
List<GenericStack> inputs = new ArrayList<>();
|
||||||
List<GenericStack> outputs = new ArrayList<>();
|
List<GenericStack> outputs = new ArrayList<>();
|
||||||
|
|
||||||
// 处理输入 - 使用客户端传入的材料选择
|
// 处理输入 - 使用客户端传入的材料选择(支持流体)
|
||||||
for (ItemStack item : selectedIngredients) {
|
for (ItemStack item : selectedIngredients) {
|
||||||
if (!item.isEmpty()) {
|
if (!item.isEmpty()) {
|
||||||
inputs.add(new GenericStack(
|
// 尝试解包 GenericStack(流体会被包装在特殊的 ItemStack 中)
|
||||||
AEItemKey.of(item),
|
GenericStack genericStack = GenericStack.unwrapItemStack(item);
|
||||||
item.getCount()
|
if (genericStack != null) {
|
||||||
));
|
// 这是一个包装的 GenericStack(可能是流体)
|
||||||
|
inputs.add(genericStack);
|
||||||
|
} else {
|
||||||
|
// 普通物品
|
||||||
|
AEItemKey itemKey = AEItemKey.of(item);
|
||||||
|
if (itemKey != null) {
|
||||||
|
inputs.add(new GenericStack(itemKey, item.getCount()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理输出
|
// 处理输出 - 使用客户端传入的输出(支持流体)
|
||||||
ItemStack result = recipe.getResultItem(player.level().registryAccess());
|
for (ItemStack item : selectedOutputs) {
|
||||||
if (!result.isEmpty()) {
|
if (!item.isEmpty()) {
|
||||||
outputs.add(new GenericStack(
|
// 尝试解包 GenericStack(流体会被包装在特殊的 ItemStack 中)
|
||||||
AEItemKey.of(result),
|
GenericStack genericStack = GenericStack.unwrapItemStack(item);
|
||||||
result.getCount()
|
if (genericStack != null) {
|
||||||
));
|
// 这是一个包装的 GenericStack(可能是流体)
|
||||||
|
outputs.add(genericStack);
|
||||||
|
} else {
|
||||||
|
// 普通物品
|
||||||
|
AEItemKey itemKey = AEItemKey.of(item);
|
||||||
|
if (itemKey != null) {
|
||||||
|
outputs.add(new GenericStack(itemKey, item.getCount()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 encodeProcessingPattern 创建处理样板
|
// 使用 encodeProcessingPattern 创建处理样板
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
package com.extendedae_plus.util;
|
package com.extendedae_plus.util;
|
||||||
|
|
||||||
|
import appeng.api.stacks.AEFluidKey;
|
||||||
|
import appeng.api.stacks.AEItemKey;
|
||||||
|
import appeng.api.stacks.GenericStack;
|
||||||
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
||||||
import mezz.jei.api.constants.RecipeTypes;
|
import mezz.jei.api.constants.RecipeTypes;
|
||||||
import mezz.jei.api.constants.VanillaTypes;
|
import mezz.jei.api.constants.VanillaTypes;
|
||||||
|
import mezz.jei.api.forge.ForgeTypes;
|
||||||
import mezz.jei.api.gui.IRecipeLayoutDrawable;
|
import mezz.jei.api.gui.IRecipeLayoutDrawable;
|
||||||
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
|
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
|
||||||
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
|
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
|
||||||
|
|
@ -17,6 +21,7 @@ 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.minecraftforge.fluids.FluidStack;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -33,9 +38,9 @@ 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物品或流体
|
||||||
* @return 相关配方信息列表(包含完整的输入输出数量)
|
* @return 相关配方信息列表(包含完整的输入输出数量)
|
||||||
*/
|
*/
|
||||||
public static List<RecipeInfo> findRecipesByIngredient(ITypedIngredient<?> ingredient) {
|
public static List<RecipeInfo> findRecipesByIngredient(ITypedIngredient<?> ingredient) {
|
||||||
|
|
@ -46,13 +51,6 @@ public class RecipeFinderUtil {
|
||||||
return List.of();
|
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();
|
IJeiHelpers jeiHelpers = jeiRuntime.getJeiHelpers();
|
||||||
IRecipeManager recipeManager = jeiRuntime.getRecipeManager();
|
IRecipeManager recipeManager = jeiRuntime.getRecipeManager();
|
||||||
IFocusFactory focusFactory = jeiHelpers.getFocusFactory();
|
IFocusFactory focusFactory = jeiHelpers.getFocusFactory();
|
||||||
|
|
@ -133,15 +131,24 @@ public class RecipeFinderUtil {
|
||||||
LOGGER.warn("[RecipeFinder] Error searching other recipe types: {}", e.getMessage());
|
LOGGER.warn("[RecipeFinder] Error searching other recipe types: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.debug("[RecipeFinder] Found {} recipes for output: {}",
|
// 记录日志
|
||||||
results.size(),
|
String ingredientDesc;
|
||||||
((ItemStack) ingredient.getIngredient()).getDescriptionId());
|
if (ingredient.getType() == VanillaTypes.ITEM_STACK) {
|
||||||
|
ingredientDesc = ((ItemStack) ingredient.getIngredient()).getDescriptionId();
|
||||||
|
} else if (ingredient.getType() == ForgeTypes.FLUID_STACK) {
|
||||||
|
FluidStack fluidStack = (FluidStack) ingredient.getIngredient();
|
||||||
|
ingredientDesc = fluidStack.getFluid().toString();
|
||||||
|
} else {
|
||||||
|
ingredientDesc = ingredient.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.debug("[RecipeFinder] Found {} recipes for output: {}", results.size(), ingredientDesc);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从配方布局中提取完整的配方信息
|
* 从配方布局中提取完整的配方信息(支持物品和流体)
|
||||||
*
|
*
|
||||||
* @param recipe 原始配方对象
|
* @param recipe 原始配方对象
|
||||||
* @param layout JEI 配方布局(包含完整的槽位和数量信息)
|
* @param layout JEI 配方布局(包含完整的槽位和数量信息)
|
||||||
|
|
@ -156,25 +163,35 @@ public class RecipeFinderUtil {
|
||||||
try {
|
try {
|
||||||
IRecipeSlotsView slotsView = layout.getRecipeSlotsView();
|
IRecipeSlotsView slotsView = layout.getRecipeSlotsView();
|
||||||
|
|
||||||
// 提取输入槽位
|
// 提取输入槽位(支持物品和流体)
|
||||||
List<IRecipeSlotView> inputSlots = slotsView.getSlotViews(RecipeIngredientRole.INPUT);
|
List<IRecipeSlotView> inputSlots = slotsView.getSlotViews(RecipeIngredientRole.INPUT);
|
||||||
List<List<ItemStack>> inputs = new ArrayList<>();
|
List<List<GenericStack>> inputs = new ArrayList<>();
|
||||||
|
|
||||||
for (IRecipeSlotView slot : inputSlots) {
|
for (IRecipeSlotView slot : inputSlots) {
|
||||||
List<ItemStack> slotItems = slot.getItemStacks()
|
List<GenericStack> slotStacks = new ArrayList<>();
|
||||||
.map(ItemStack::copy) // 复制以保留数量信息
|
|
||||||
.toList();
|
// 提取所有 ITypedIngredient
|
||||||
inputs.add(slotItems);
|
for (ITypedIngredient<?> typedIngredient : slot.getAllIngredients().toList()) {
|
||||||
|
GenericStack genericStack = convertToGenericStack(typedIngredient);
|
||||||
|
if (genericStack != null) {
|
||||||
|
slotStacks.add(genericStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.add(slotStacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取输出槽位
|
// 提取输出槽位(支持物品和流体)
|
||||||
List<IRecipeSlotView> outputSlots = slotsView.getSlotViews(RecipeIngredientRole.OUTPUT);
|
List<IRecipeSlotView> outputSlots = slotsView.getSlotViews(RecipeIngredientRole.OUTPUT);
|
||||||
List<ItemStack> outputs = new ArrayList<>();
|
List<GenericStack> outputs = new ArrayList<>();
|
||||||
|
|
||||||
for (IRecipeSlotView slot : outputSlots) {
|
for (IRecipeSlotView slot : outputSlots) {
|
||||||
slot.getItemStacks()
|
for (ITypedIngredient<?> typedIngredient : slot.getAllIngredients().toList()) {
|
||||||
.map(ItemStack::copy) // 复制以保留数量信息
|
GenericStack genericStack = convertToGenericStack(typedIngredient);
|
||||||
.forEach(outputs::add);
|
if (genericStack != null) {
|
||||||
|
outputs.add(genericStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RecipeInfo(recipe, isCrafting, inputs, outputs);
|
return new RecipeInfo(recipe, isCrafting, inputs, outputs);
|
||||||
|
|
@ -186,6 +203,37 @@ public class RecipeFinderUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JEI 的 ITypedIngredient 转换为 AE2 的 GenericStack
|
||||||
|
*
|
||||||
|
* @param typedIngredient JEI 类型化材料
|
||||||
|
* @return AE2 GenericStack,如果不支持的类型返回 null
|
||||||
|
*/
|
||||||
|
private static GenericStack convertToGenericStack(ITypedIngredient<?> typedIngredient) {
|
||||||
|
// 处理物品
|
||||||
|
if (typedIngredient.getType() == VanillaTypes.ITEM_STACK) {
|
||||||
|
ItemStack itemStack = (ItemStack) typedIngredient.getIngredient();
|
||||||
|
if (!itemStack.isEmpty()) {
|
||||||
|
AEItemKey itemKey = AEItemKey.of(itemStack);
|
||||||
|
if (itemKey != null) {
|
||||||
|
return new GenericStack(itemKey, itemStack.getCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理流体
|
||||||
|
else if (typedIngredient.getType() == ForgeTypes.FLUID_STACK) {
|
||||||
|
FluidStack fluidStack = (FluidStack) typedIngredient.getIngredient();
|
||||||
|
if (!fluidStack.isEmpty()) {
|
||||||
|
AEFluidKey fluidKey = AEFluidKey.of(fluidStack);
|
||||||
|
if (fluidKey != null) {
|
||||||
|
return new GenericStack(fluidKey, fluidStack.getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择最佳配方(优先选择工作台配方)
|
* 选择最佳配方(优先选择工作台配方)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,30 @@
|
||||||
package com.extendedae_plus.util;
|
package com.extendedae_plus.util;
|
||||||
|
|
||||||
|
import appeng.api.stacks.AEFluidKey;
|
||||||
|
import appeng.api.stacks.AEItemKey;
|
||||||
|
import appeng.api.stacks.GenericStack;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.Recipe;
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配方完整信息
|
* 配方完整信息
|
||||||
*
|
*
|
||||||
* <p>包含配方的所有输入材料(带数量)和输出物品</p>
|
* <p>包含配方的所有输入材料(带数量)和输出物品/流体</p>
|
||||||
*/
|
*/
|
||||||
public class RecipeInfo {
|
public class RecipeInfo {
|
||||||
private final Recipe<?> recipe;
|
private final Recipe<?> recipe;
|
||||||
private final boolean isCraftingRecipe;
|
private final boolean isCraftingRecipe;
|
||||||
private final List<List<ItemStack>> inputs; // 每个槽位的所有可能物品(包含数量)
|
private final List<List<GenericStack>> inputs; // 每个槽位的所有可能材料(物品或流体,包含数量)
|
||||||
private final List<ItemStack> outputs; // 输出物品(包含数量)
|
private final List<GenericStack> outputs; // 输出材料(物品或流体,包含数量)
|
||||||
|
|
||||||
public RecipeInfo(
|
public RecipeInfo(
|
||||||
Recipe<?> recipe,
|
Recipe<?> recipe,
|
||||||
boolean isCraftingRecipe,
|
boolean isCraftingRecipe,
|
||||||
List<List<ItemStack>> inputs,
|
List<List<GenericStack>> inputs,
|
||||||
List<ItemStack> outputs
|
List<GenericStack> outputs
|
||||||
) {
|
) {
|
||||||
this.recipe = recipe;
|
this.recipe = recipe;
|
||||||
this.isCraftingRecipe = isCraftingRecipe;
|
this.isCraftingRecipe = isCraftingRecipe;
|
||||||
|
|
@ -45,18 +49,18 @@ public class RecipeInfo {
|
||||||
/**
|
/**
|
||||||
* 获取输入材料列表
|
* 获取输入材料列表
|
||||||
*
|
*
|
||||||
* @return 每个槽位的所有可能物品列表(包含数量)
|
* @return 每个槽位的所有可能材料列表(物品或流体,包含数量)
|
||||||
*/
|
*/
|
||||||
public List<List<ItemStack>> getInputs() {
|
public List<List<GenericStack>> getInputs() {
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取输出物品列表
|
* 获取输出材料列表
|
||||||
*
|
*
|
||||||
* @return 输出物品列表(包含数量)
|
* @return 输出材料列表(物品或流体,包含数量)
|
||||||
*/
|
*/
|
||||||
public List<ItemStack> getOutputs() {
|
public List<GenericStack> getOutputs() {
|
||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,33 +68,60 @@ public class RecipeInfo {
|
||||||
* 应用 JEI 书签优先级选择最佳输入材料
|
* 应用 JEI 书签优先级选择最佳输入材料
|
||||||
*
|
*
|
||||||
* @param bookmarkPriorities 书签优先级映射(物品 -> 优先级,数值越小优先级越高)
|
* @param bookmarkPriorities 书签优先级映射(物品 -> 优先级,数值越小优先级越高)
|
||||||
* @return 选择的材料列表(每个槽位一个物品)
|
* @return 选择的材料列表(每个槽位一个材料,转换为 ItemStack 用于网络传输)
|
||||||
*/
|
*/
|
||||||
public List<ItemStack> selectBestInputs(java.util.Map<net.minecraft.world.item.Item, Integer> bookmarkPriorities) {
|
public List<ItemStack> selectBestInputs(java.util.Map<net.minecraft.world.item.Item, Integer> bookmarkPriorities) {
|
||||||
java.util.List<ItemStack> selected = new java.util.ArrayList<>();
|
List<ItemStack> selected = new ArrayList<>();
|
||||||
|
|
||||||
for (List<ItemStack> slotOptions : inputs) {
|
for (List<GenericStack> slotOptions : inputs) {
|
||||||
if (slotOptions.isEmpty()) {
|
if (slotOptions.isEmpty()) {
|
||||||
selected.add(ItemStack.EMPTY);
|
selected.add(ItemStack.EMPTY);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择优先级最高的物品(如果都不在书签中,选第一个)
|
// 选择优先级最高的材料(如果都不在书签中,选第一个)
|
||||||
ItemStack best = slotOptions.get(0);
|
GenericStack best = slotOptions.get(0);
|
||||||
int bestPriority = bookmarkPriorities.getOrDefault(best.getItem(), Integer.MAX_VALUE);
|
int bestPriority = getPriority(best, bookmarkPriorities);
|
||||||
|
|
||||||
for (int i = 1; i < slotOptions.size(); i++) {
|
for (int i = 1; i < slotOptions.size(); i++) {
|
||||||
ItemStack option = slotOptions.get(i);
|
GenericStack option = slotOptions.get(i);
|
||||||
int priority = bookmarkPriorities.getOrDefault(option.getItem(), Integer.MAX_VALUE);
|
int priority = getPriority(option, bookmarkPriorities);
|
||||||
if (priority < bestPriority) {
|
if (priority < bestPriority) {
|
||||||
bestPriority = priority;
|
bestPriority = priority;
|
||||||
best = option;
|
best = option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selected.add(best.copy());
|
// 转换为 ItemStack(流体会被包装)
|
||||||
|
selected.add(toItemStack(best));
|
||||||
}
|
}
|
||||||
|
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取材料的优先级
|
||||||
|
*/
|
||||||
|
private int getPriority(GenericStack stack, java.util.Map<net.minecraft.world.item.Item, Integer> priorities) {
|
||||||
|
if (stack.what() instanceof AEItemKey itemKey) {
|
||||||
|
return priorities.getOrDefault(itemKey.getItem(), Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
// 流体没有书签优先级,返回默认值
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 GenericStack 转换为 ItemStack
|
||||||
|
*
|
||||||
|
* <p>物品直接转换,流体会被包装成 GenericStack.wrapInItemStack</p>
|
||||||
|
*/
|
||||||
|
private ItemStack toItemStack(GenericStack stack) {
|
||||||
|
if (stack.what() instanceof AEItemKey itemKey) {
|
||||||
|
return itemKey.toStack((int) stack.amount());
|
||||||
|
} else if (stack.what() instanceof AEFluidKey) {
|
||||||
|
// 流体需要包装成特殊的 ItemStack
|
||||||
|
return GenericStack.wrapInItemStack(stack);
|
||||||
|
}
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user