合成书签分支完成
This commit is contained in:
parent
974c09be78
commit
ab7664e4a6
|
|
@ -161,7 +161,110 @@ public class CtrlQPatternKeyHandler {
|
|||
* @param recipeBookmark 配方书签对象
|
||||
*/
|
||||
private static void handleCraftingRecipeBookmark(Object recipeBookmark) {
|
||||
System.out.println("hecheng");
|
||||
try {
|
||||
// 1. 获取配方ID
|
||||
var getRecipeUidMethod = recipeBookmark.getClass().getMethod("getRecipeUid");
|
||||
net.minecraft.resources.ResourceLocation recipeId =
|
||||
(net.minecraft.resources.ResourceLocation) getRecipeUidMethod.invoke(recipeBookmark);
|
||||
|
||||
if (recipeId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取Minecraft实例
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.level == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 验证配方存在
|
||||
var recipeManager = mc.level.getRecipeManager();
|
||||
var recipeOpt = recipeManager.byKey(recipeId);
|
||||
|
||||
if (recipeOpt.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.recipe_not_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 从JEI获取配方的详细信息(输入输出槽位)
|
||||
// 使用RecipeBookmark的getRecipe方法获取配方对象
|
||||
var getRecipeMethod = recipeBookmark.getClass().getMethod("getRecipe");
|
||||
Object recipe = getRecipeMethod.invoke(recipeBookmark);
|
||||
|
||||
if (recipe == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 通过JEI的RecipeManager获取配方布局信息
|
||||
mezz.jei.api.runtime.IJeiRuntime jeiRuntime = JeiRuntimeProxy.get();
|
||||
if (jeiRuntime == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. 获取配方类别
|
||||
var getRecipeCategoryMethod = recipeBookmark.getClass().getMethod("getRecipeCategory");
|
||||
Object recipeCategory = getRecipeCategoryMethod.invoke(recipeBookmark);
|
||||
|
||||
// 7. 创建RecipeInfo来应用JEI书签优先级
|
||||
List<RecipeInfo> recipeInfos = RecipeFinderUtil.findRecipesByIngredient(
|
||||
JeiRuntimeProxy.getIngredientUnderMouse().orElse(null)
|
||||
);
|
||||
|
||||
// 如果找不到,尝试通过配方输出物品查找
|
||||
if (recipeInfos.isEmpty()) {
|
||||
var getRecipeOutputMethod = recipeBookmark.getClass().getMethod("getRecipeOutput");
|
||||
Object recipeOutput = getRecipeOutputMethod.invoke(recipeBookmark);
|
||||
|
||||
if (recipeOutput instanceof mezz.jei.api.ingredients.ITypedIngredient<?> typedIngredient) {
|
||||
recipeInfos = RecipeFinderUtil.findRecipesByIngredient(typedIngredient);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipeInfos.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.no_recipes_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 8. 找到匹配的RecipeInfo
|
||||
RecipeInfo matchingRecipeInfo = null;
|
||||
for (RecipeInfo info : recipeInfos) {
|
||||
if (info.getRecipe().getId().equals(recipeId)) {
|
||||
matchingRecipeInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingRecipeInfo == null) {
|
||||
matchingRecipeInfo = recipeInfos.get(0);
|
||||
}
|
||||
|
||||
// 9. 应用JEI书签优先级选择材料
|
||||
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(matchingRecipeInfo);
|
||||
|
||||
// 10. 获取输出材料
|
||||
List<ItemStack> selectedOutputs = convertOutputsToItemStacks(matchingRecipeInfo);
|
||||
|
||||
// 11. 发送网络包创建样板并上传到装配矩阵
|
||||
ModNetwork.CHANNEL.sendToServer(new com.extendedae_plus.network.pattern.CreateAndUploadPatternC2SPacket(
|
||||
recipeId,
|
||||
matchingRecipeInfo.isCraftingRecipe(),
|
||||
selectedIngredients,
|
||||
selectedOutputs
|
||||
));
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket
|
|||
import com.extendedae_plus.network.crafting.OpenCraftFromJeiC2SPacket;
|
||||
import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CreateCtrlQPatternC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CreateAndUploadPatternC2SPacket;
|
||||
import com.extendedae_plus.network.provider.*;
|
||||
import com.extendedae_plus.network.upload.EncodeWithShiftFlagC2SPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
|
@ -175,6 +176,12 @@ public final class ModNetwork {
|
|||
.decoder(CreateCtrlQPatternC2SPacket::decode)
|
||||
.consumerNetworkThread(CreateCtrlQPatternC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(CreateAndUploadPatternC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(CreateAndUploadPatternC2SPacket::encode)
|
||||
.decoder(CreateAndUploadPatternC2SPacket::decode)
|
||||
.consumerNetworkThread(CreateAndUploadPatternC2SPacket::handle)
|
||||
.add();
|
||||
}
|
||||
|
||||
private static int nextId() { return id++; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,297 @@
|
|||
package com.extendedae_plus.network.pattern;
|
||||
|
||||
import appeng.api.crafting.PatternDetailsHelper;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.energy.IEnergyService;
|
||||
import appeng.api.stacks.AEItemKey;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.api.storage.MEStorage;
|
||||
import appeng.api.storage.StorageHelper;
|
||||
import appeng.core.definitions.AEItems;
|
||||
import appeng.items.tools.powered.WirelessCraftingTerminalItem;
|
||||
import appeng.items.tools.powered.WirelessTerminalItem;
|
||||
import appeng.me.helpers.PlayerSource;
|
||||
import com.extendedae_plus.util.uploadPattern.MatrixUploadUtil;
|
||||
import com.extendedae_plus.util.wireless.WirelessTerminalLocator;
|
||||
import de.mari_023.ae2wtlib.terminal.WTMenuHost;
|
||||
import de.mari_023.ae2wtlib.wut.WTDefinition;
|
||||
import de.mari_023.ae2wtlib.wut.WUTHandler;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.CraftingRecipe;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* C2S: 创建样板并上传到装配矩阵
|
||||
*
|
||||
* <p>从客户端发送配方ID、选择的材料和输出到服务器,服务器消耗空白样板并创建编码样板,然后直接上传到装配矩阵</p>
|
||||
*/
|
||||
public class CreateAndUploadPatternC2SPacket {
|
||||
|
||||
private final ResourceLocation recipeId;
|
||||
private final boolean isCraftingPattern;
|
||||
private final List<ItemStack> selectedIngredients;
|
||||
private final List<ItemStack> outputs;
|
||||
|
||||
public CreateAndUploadPatternC2SPacket(ResourceLocation recipeId, boolean isCraftingPattern, List<ItemStack> selectedIngredients, List<ItemStack> outputs) {
|
||||
this.recipeId = recipeId;
|
||||
this.isCraftingPattern = isCraftingPattern;
|
||||
this.selectedIngredients = selectedIngredients;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
public static void encode(CreateAndUploadPatternC2SPacket msg, FriendlyByteBuf buf) {
|
||||
buf.writeResourceLocation(msg.recipeId);
|
||||
buf.writeBoolean(msg.isCraftingPattern);
|
||||
buf.writeInt(msg.selectedIngredients.size());
|
||||
for (ItemStack stack : msg.selectedIngredients) {
|
||||
buf.writeItem(stack);
|
||||
}
|
||||
buf.writeInt(msg.outputs.size());
|
||||
for (ItemStack stack : msg.outputs) {
|
||||
buf.writeItem(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public static CreateAndUploadPatternC2SPacket decode(FriendlyByteBuf buf) {
|
||||
ResourceLocation recipeId = buf.readResourceLocation();
|
||||
boolean isCraftingPattern = buf.readBoolean();
|
||||
int ingredientCount = buf.readInt();
|
||||
List<ItemStack> ingredients = new ArrayList<>();
|
||||
for (int i = 0; i < ingredientCount; i++) {
|
||||
ingredients.add(buf.readItem());
|
||||
}
|
||||
int outputCount = buf.readInt();
|
||||
List<ItemStack> outputs = new ArrayList<>();
|
||||
for (int i = 0; i < outputCount; i++) {
|
||||
outputs.add(buf.readItem());
|
||||
}
|
||||
return new CreateAndUploadPatternC2SPacket(recipeId, isCraftingPattern, ingredients, outputs);
|
||||
}
|
||||
|
||||
public static void handle(CreateAndUploadPatternC2SPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||
var ctx = ctxSupplier.get();
|
||||
ctx.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 验证配方存在
|
||||
RecipeManager recipeManager = player.level().getRecipeManager();
|
||||
var recipeOpt = recipeManager.byKey(msg.recipeId);
|
||||
|
||||
if (recipeOpt.isEmpty()) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.recipe_not_found"),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Recipe<?> recipe = recipeOpt.get();
|
||||
|
||||
// 2. 获取AE网络
|
||||
IGrid grid = getPlayerGrid(player);
|
||||
if (grid == null) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.no_network"),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 消耗空白样板
|
||||
if (!consumeBlankPattern(player, grid)) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.no_blank_pattern"),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 创建样板
|
||||
ItemStack pattern = createPattern(recipe, msg.isCraftingPattern, msg.selectedIngredients, msg.outputs, player);
|
||||
|
||||
if (pattern.isEmpty()) {
|
||||
// 创建失败,退还空白样板到网络
|
||||
refundBlankPattern(player, grid);
|
||||
player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.pattern_creation_failed"),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 上传样板到装配矩阵
|
||||
boolean uploaded = MatrixUploadUtil.uploadPatternToMatrix(player, pattern, grid);
|
||||
|
||||
if (!uploaded) {
|
||||
// 上传失败,退还空白样板到网络
|
||||
refundBlankPattern(player, grid);
|
||||
}
|
||||
});
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家的AE网络
|
||||
*/
|
||||
private static IGrid getPlayerGrid(ServerPlayer player) {
|
||||
WirelessTerminalLocator.LocatedTerminal located = WirelessTerminalLocator.find(player);
|
||||
ItemStack terminal = located.stack;
|
||||
if (terminal.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String curiosSlotId = located.getCuriosSlotId();
|
||||
int curiosIndex = located.getCuriosIndex();
|
||||
|
||||
if (curiosSlotId != null && curiosIndex >= 0) {
|
||||
try {
|
||||
String current = WUTHandler.getCurrentTerminal(terminal);
|
||||
WTDefinition def = WUTHandler.wirelessTerminals.get(current);
|
||||
if (def != null) {
|
||||
WTMenuHost wtHost = def.wTMenuHostFactory().create(player, null, terminal, (p, sub) -> {});
|
||||
if (wtHost != null) {
|
||||
IGridNode node = wtHost.getActionableNode();
|
||||
if (node != null) {
|
||||
return node.getGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
WirelessTerminalItem wt = terminal.getItem() instanceof WirelessTerminalItem t ? t : null;
|
||||
if (wt != null) {
|
||||
return wt.getLinkedGrid(terminal, player.serverLevel(), player);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消耗空白样板:优先从AE网络提取
|
||||
*/
|
||||
private static boolean consumeBlankPattern(ServerPlayer player, IGrid grid) {
|
||||
AEItemKey blankPatternKey = AEItemKey.of(AEItems.BLANK_PATTERN.stack());
|
||||
IEnergyService energy = grid.getEnergyService();
|
||||
MEStorage storage = grid.getStorageService().getInventory();
|
||||
|
||||
long extracted = StorageHelper.poweredExtraction(
|
||||
energy,
|
||||
storage,
|
||||
blankPatternKey,
|
||||
1,
|
||||
new PlayerSource(player)
|
||||
);
|
||||
|
||||
return extracted > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退还空白样板到网络
|
||||
*/
|
||||
private static void refundBlankPattern(ServerPlayer player, IGrid grid) {
|
||||
AEItemKey blankPatternKey = AEItemKey.of(AEItems.BLANK_PATTERN.stack());
|
||||
IEnergyService energy = grid.getEnergyService();
|
||||
MEStorage storage = grid.getStorageService().getInventory();
|
||||
|
||||
StorageHelper.poweredInsert(
|
||||
energy,
|
||||
storage,
|
||||
blankPatternKey,
|
||||
1,
|
||||
new PlayerSource(player)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配方创建样板
|
||||
*/
|
||||
private static ItemStack createPattern(Recipe<?> recipe, boolean isCrafting, List<ItemStack> selectedIngredients, List<ItemStack> selectedOutputs, ServerPlayer player) {
|
||||
try {
|
||||
if (isCrafting && recipe instanceof CraftingRecipe craftingRecipe) {
|
||||
// 合成样板
|
||||
ItemStack[] inputs = new ItemStack[9];
|
||||
for (int i = 0; i < 9; i++) {
|
||||
if (i < selectedIngredients.size()) {
|
||||
inputs[i] = selectedIngredients.get(i).copy();
|
||||
} else {
|
||||
inputs[i] = ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack output = recipe.getResultItem(player.level().registryAccess()).copy();
|
||||
|
||||
ItemStack encodedPattern = PatternDetailsHelper.encodeCraftingPattern(
|
||||
craftingRecipe,
|
||||
inputs,
|
||||
output,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
encodedPattern.getOrCreateTag().putString("encodePlayer", player.getName().getString());
|
||||
return encodedPattern;
|
||||
|
||||
} else {
|
||||
// 处理样板
|
||||
List<GenericStack> inputs = new ArrayList<>();
|
||||
List<GenericStack> outputs = new ArrayList<>();
|
||||
|
||||
for (ItemStack item : selectedIngredients) {
|
||||
if (!item.isEmpty()) {
|
||||
GenericStack genericStack = GenericStack.unwrapItemStack(item);
|
||||
if (genericStack != null) {
|
||||
inputs.add(genericStack);
|
||||
} else {
|
||||
AEItemKey itemKey = AEItemKey.of(item);
|
||||
if (itemKey != null) {
|
||||
inputs.add(new GenericStack(itemKey, item.getCount()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack item : selectedOutputs) {
|
||||
if (!item.isEmpty()) {
|
||||
GenericStack genericStack = GenericStack.unwrapItemStack(item);
|
||||
if (genericStack != null) {
|
||||
outputs.add(genericStack);
|
||||
} else {
|
||||
AEItemKey itemKey = AEItemKey.of(item);
|
||||
if (itemKey != null) {
|
||||
outputs.add(new GenericStack(itemKey, item.getCount()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack encodedPattern = PatternDetailsHelper.encodeProcessingPattern(
|
||||
inputs.toArray(new GenericStack[0]),
|
||||
outputs.toArray(new GenericStack[0])
|
||||
);
|
||||
|
||||
encodedPattern.getOrCreateTag().putString("encodePlayer", player.getName().getString());
|
||||
return encodedPattern;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,6 +87,61 @@ public final class MatrixUploadUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 直接上传已创建的样板到装配矩阵(不从菜单读取)
|
||||
*
|
||||
* @param player 服务器玩家
|
||||
* @param pattern 已编码的样板
|
||||
* @param grid AE网络
|
||||
* @return 是否上传成功
|
||||
*/
|
||||
public static boolean uploadPatternToMatrix(ServerPlayer player, ItemStack pattern, IGrid grid) {
|
||||
if (player == null || pattern.isEmpty() || grid == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证是否为已编码的样板
|
||||
if (!PatternDetailsHelper.isEncodedPattern(pattern)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 仅允许"合成/锻造台/切石机样板"
|
||||
IPatternDetails details = PatternDetailsHelper.decodePattern(pattern, player.level());
|
||||
if (!(details instanceof AECraftingPattern
|
||||
|| details instanceof AESmithingTablePattern
|
||||
|| details instanceof AEStonecuttingPattern)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack toInsert = pattern.copy();
|
||||
|
||||
// 收集所有可用的装配矩阵(图样模块)内部库存并逐一尝试(遵循其过滤规则)
|
||||
List<InternalInventory> inventories = findAllMatrixPatternInventories(grid);
|
||||
|
||||
// 在尝试上传之前,检查装配矩阵是否已经存在相同样板(物品与NBT完全一致)
|
||||
if (matrixContainsPattern(inventories, pattern)) {
|
||||
// 直接提醒并跳过上传
|
||||
sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.repetition"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试插入
|
||||
for (InternalInventory inv : inventories) {
|
||||
if (inv == null) continue;
|
||||
ItemStack remain = inv.addItems(toInsert);
|
||||
if (remain.getCount() < pattern.getCount()) {
|
||||
// 上传成功
|
||||
sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.success"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 所有矩阵都满了
|
||||
sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.full"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 在给定 AE Grid 中收集所有已成型且在线的装配矩阵“样板核心”的用于外部插入的内部库存
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user