From 7fe6d63c03010d9aeaa267b1333b60cf9aec652d Mon Sep 17 00:00:00 2001 From: XianYuFish001 Date: Thu, 25 Sep 2025 13:30:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BF=AB=E6=8D=B7=E4=BB=8EEMI=E9=85=8D?= =?UTF-8?q?=E6=96=B9=E6=A0=91=E7=95=8C=E9=9D=A2=E7=BC=96=E5=86=99/?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=A0=B7=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../com/extendedae_plus/ExtendedAEPlus.java | 1 - .../{network => init}/ModNetwork.java | 6 +- .../integration/emi/HandlerBoMRecipes.java | 96 +++++++++++++++++ .../integration/emi/PatternFillingHelper.java | 100 ++++++++++++++++++ .../mixin/emi/MixinBoMScreen.java | 90 ++++++++++++++++ .../network/C2SPacketStoneCuttingID.java | 31 ++++++ .../resources/extendedae_plus.mixins.json | 1 + 8 files changed, 324 insertions(+), 3 deletions(-) rename src/main/java/com/extendedae_plus/{network => init}/ModNetwork.java (93%) create mode 100644 src/main/java/com/extendedae_plus/integration/emi/HandlerBoMRecipes.java create mode 100644 src/main/java/com/extendedae_plus/integration/emi/PatternFillingHelper.java create mode 100644 src/main/java/com/extendedae_plus/mixin/emi/MixinBoMScreen.java create mode 100644 src/main/java/com/extendedae_plus/network/C2SPacketStoneCuttingID.java diff --git a/gradle.properties b/gradle.properties index 33f071a..4345f0a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ loader_version_range=[1,) mod_id=extendedae_plus mod_name=ExtendedAE-Plus mod_license=LGPL-3.0-or-later -mod_version=1.0.0fix +mod_version=1.0.1 mod_group_id=com.extendedae_plus mod_authors=GaLicn, XianYuFish001 mod_description= diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 703b64c..6a792bf 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -7,7 +7,6 @@ import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler; import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellInventory; import com.extendedae_plus.config.ModConfig; import com.extendedae_plus.init.*; -import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.util.storage.InfinityStorageManager; import com.mojang.logging.LogUtils; import net.minecraft.core.registries.BuiltInRegistries; diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java similarity index 93% rename from src/main/java/com/extendedae_plus/network/ModNetwork.java rename to src/main/java/com/extendedae_plus/init/ModNetwork.java index 632ca17..9c3c262 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -1,6 +1,7 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.network.*; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; public class ModNetwork { @@ -39,5 +40,8 @@ public class ModNetwork { registrar.playToServer(C2SPacketTargetKeyTriggered.TYPE, C2SPacketTargetKeyTriggered.STREAM_CODEC, C2SPacketTargetKeyTriggered::handle); + registrar.playToServer(C2SPacketStoneCuttingID.TYPE, + C2SPacketStoneCuttingID.STREAM_CODEC, + C2SPacketStoneCuttingID::handle); } } diff --git a/src/main/java/com/extendedae_plus/integration/emi/HandlerBoMRecipes.java b/src/main/java/com/extendedae_plus/integration/emi/HandlerBoMRecipes.java new file mode 100644 index 0000000..a10702b --- /dev/null +++ b/src/main/java/com/extendedae_plus/integration/emi/HandlerBoMRecipes.java @@ -0,0 +1,96 @@ +package com.extendedae_plus.integration.emi; + +import appeng.api.stacks.GenericStack; +import appeng.integration.modules.emi.EmiStackHelper; +import dev.emi.emi.api.recipe.EmiRecipe; +import dev.emi.emi.api.stack.EmiStack; +import dev.emi.emi.bom.FoldState; +import dev.emi.emi.bom.MaterialNode; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.RecipeType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class HandlerBoMRecipes { + public static HashMap collectInputs(MaterialNode parentNode) { + var recipe = parentNode.recipe; + if (parentNode.state != FoldState.EXPANDED) return new HashMap<>(); + if (parentNode.children == null) return new HashMap<>(); + + HashMap results = new HashMap<>(); + var type = recipe.getBackingRecipe().value().getType(); + + parentNode.children.forEach(child -> { + EmiStack stack; + + if (child.recipe != null && type != RecipeType.STONECUTTING) + stack = child.recipe.getOutputs().getFirst().getEmiStacks().getFirst(); + else stack = child.ingredient.getEmiStacks().getFirst(); + + recipe.getInputs().forEach(input -> { + if (input.getEmiStacks().contains(stack)) { + stack.setAmount(input.getEmiStacks().getFirst().getAmount()); + GenericStack genericStack = EmiStackHelper.toGenericStack(stack); + if (genericStack == null) return; + + results.put(input.getEmiStacks().getFirst().getId(), genericStack); + } + }); + }); + return results; + } + +// public static void handle(C2SPacketStoneCuttingID packet, ServerPlayer player) { +// if (!(player.containerMenu instanceof PatternEncodingTermMenu menu)) return; +//// RecipeHolder recipeHolder = player.level().getRecipeManager() +//// .byKey(packet.recipeID()).orElse(null); +// EmiRecipe recipe = EmiApi.getRecipeManager() +// .getRecipe(packet.recipeID()); +// +// if (recipe == null) return; +// +// try { +// menu.clear(); +// List> modifiedInputs = updateRecipe(recipe, packet.batches(), packet.selectedStacks()); +// +// // 遍历原配方的每个输入 +// for (EmiIngredient originalInput : recipe.getInputs()) { +// if (!originalInput.getEmiStacks().isEmpty()) { +// EmiStack firstStack = originalInput.getEmiStacks().getFirst(); +// ResourceLocation stackId = firstStack.getId(); +// +// if (packet.selectedStacks().containsKey(stackId)) +// modifiedInputs.add(List.of( +// packet.selectedStacks().getOrDefault(stackId, EmiStackHelper.toGenericStack(firstStack)))); +// } +// } +// +// // 获取输出(保持不变) +// List outputs = EmiStackHelper.ofOutputs(recipe); +// +// // 编码修改后的配方 +//// EncodingHelper.encodeProcessingRecipe(menu, modifiedInputs, outputs); +// } catch (Exception e) { +// ExtendedAEPlus.LOGGER.debug("encode failed:", e); +// } +// } + + public static List> updateRecipe(EmiRecipe original, long batches, + HashMap selectedInputs) { + List> modifiedInputs = new ArrayList<>(); + + EmiStackHelper.ofInputs(original).forEach(stacks -> { + if (!stacks.isEmpty()) { + GenericStack originStack = stacks.getFirst(); + ResourceLocation stackId = originStack.what().getId(); + + if (selectedInputs.containsKey(stackId)) + modifiedInputs.add(List.of( + selectedInputs.getOrDefault(stackId, originStack))); + } else modifiedInputs.add(List.of()); + }); + return modifiedInputs; + } +} diff --git a/src/main/java/com/extendedae_plus/integration/emi/PatternFillingHelper.java b/src/main/java/com/extendedae_plus/integration/emi/PatternFillingHelper.java new file mode 100644 index 0000000..12fc5f8 --- /dev/null +++ b/src/main/java/com/extendedae_plus/integration/emi/PatternFillingHelper.java @@ -0,0 +1,100 @@ +package com.extendedae_plus.integration.emi; + +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.GenericStack; +import appeng.core.network.ServerboundPacket; +import appeng.core.network.serverbound.InventoryActionPacket; +import appeng.helpers.InventoryAction; +import appeng.menu.me.items.PatternEncodingTermMenu; +import appeng.parts.encoding.EncodingMode; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeType; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +public class PatternFillingHelper { + public static void encodeCraftingRecipe(PatternEncodingTermMenu menu, + @Nullable RecipeHolder recipe, + List> genericIngredients, + Predicate visiblePredicate) { + if (recipe != null && recipe.value().getType().equals(RecipeType.STONECUTTING)) { + menu.setMode(EncodingMode.STONECUTTING); + menu.setStonecuttingRecipeId(recipe.id()); + } else if (recipe != null && recipe.value().getType().equals(RecipeType.SMITHING)) + menu.setMode(EncodingMode.SMITHING_TABLE); + else menu.setMode(EncodingMode.CRAFTING); + + var encodedInputs = NonNullList.withSize(menu.getCraftingGridSlots().length, ItemStack.EMPTY); + + for (int slot = 0; slot < genericIngredients.size(); slot++) { + var genericIngredient = genericIngredients.get(slot); + if (genericIngredient.isEmpty()) continue; + + GenericStack selectedStack = null; + for (var stack : genericIngredient) { + if (stack.what() instanceof AEItemKey itemKey) { + ItemStack itemStack = itemKey.toStack(); + if (visiblePredicate.test(itemStack)) { + selectedStack = stack; + break; + } + } + } + + if (selectedStack == null) selectedStack = genericIngredient.getFirst(); + + if (selectedStack.what() instanceof AEItemKey itemKey) + encodedInputs.set(slot, itemKey.toStack()); + else encodedInputs.set(slot, GenericStack.wrapInItemStack(selectedStack.what(), 1)); + } + + for (int i = 0; i < encodedInputs.size(); i++) { + ItemStack encodedInput = encodedInputs.get(i); + ServerboundPacket message = new InventoryActionPacket( + InventoryAction.SET_FILTER, menu.getCraftingGridSlots()[i].index, encodedInput); + PacketDistributor.sendToServer(message); + } + + for (var outputSlot : menu.getProcessingOutputSlots()) { + ServerboundPacket message = new InventoryActionPacket( + InventoryAction.SET_FILTER, outputSlot.index, ItemStack.EMPTY); + PacketDistributor.sendToServer(message); + } + } + + public static void encodeProcessingRecipe(PatternEncodingTermMenu menu, List> genericIngredients, + List genericResults) { + menu.setMode(EncodingMode.PROCESSING); + + for (int i = 0; i < menu.getProcessingInputSlots().length; i++) { + ItemStack stackToSet = ItemStack.EMPTY; + + if (i < genericIngredients.size() && !genericIngredients.get(i).isEmpty()) { + GenericStack selectedStack = genericIngredients.get(i).getFirst(); + stackToSet = GenericStack.wrapInItemStack(selectedStack); + } + + ServerboundPacket message = new InventoryActionPacket( + InventoryAction.SET_FILTER, menu.getProcessingInputSlots()[i].index, stackToSet); + PacketDistributor.sendToServer(message); + } + + for (int i = 0; i < menu.getProcessingOutputSlots().length; i++) { + ItemStack stackToSet = ItemStack.EMPTY; + + if (i < genericResults.size()) { + GenericStack resultStack = genericResults.get(i); + stackToSet = GenericStack.wrapInItemStack(resultStack); + } + + ServerboundPacket message = new InventoryActionPacket( + InventoryAction.SET_FILTER, menu.getProcessingOutputSlots()[i].index, stackToSet); + PacketDistributor.sendToServer(message); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/emi/MixinBoMScreen.java b/src/main/java/com/extendedae_plus/mixin/emi/MixinBoMScreen.java new file mode 100644 index 0000000..60a4152 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/emi/MixinBoMScreen.java @@ -0,0 +1,90 @@ +package com.extendedae_plus.mixin.emi; + +import appeng.api.stacks.GenericStack; +import appeng.integration.modules.emi.EmiStackHelper; +import appeng.menu.me.items.PatternEncodingTermMenu; +import com.extendedae_plus.integration.emi.HandlerBoMRecipes; +import com.extendedae_plus.integration.emi.PatternFillingHelper; +import com.extendedae_plus.network.RequestProvidersListC2SPacket; +import com.extendedae_plus.util.ExtendedAEPatternUploadUtil; +import dev.emi.emi.api.stack.EmiStack; +import dev.emi.emi.bom.BoM; +import dev.emi.emi.bom.MaterialNode; +import dev.emi.emi.screen.BoMScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.RecipeType; +import net.neoforged.neoforge.network.PacketDistributor; +import org.lwjgl.glfw.GLFW; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@Mixin(BoMScreen.class) +public abstract class MixinBoMScreen { + @Inject(method = "mouseClicked", at = @At("HEAD")) + private void onMouseClick(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { + if (!(button == GLFW.GLFW_MOUSE_BUTTON_MIDDLE && Screen.hasControlDown())) return; + try { + var self = (BoMScreen)(Object) this; + Object hover = self.getHoveredStack((int) mouseX, (int) mouseY); + HashMap selectedInputs; + + // 救救孩子😭😭😭为什么private内部类套内部类aaa + // Mixins你们好啊, 我是reflection大王, 我要来破坏代码兼容性了😈😈😈 + // 你知道吗: 其实以前这里套了两层reflection😋😋😋 + if (hover != null) { + Field nodeField = hover.getClass().getDeclaredField("node"); + nodeField.setAccessible(true); + Object nodeObject = nodeField.get(hover); + MaterialNode node = (MaterialNode) nodeObject; + + if (node.recipe != null) { + if (Minecraft.getInstance().player.containerMenu instanceof PatternEncodingTermMenu menu) { + boolean isCraftingRecipe = false; + if (node.recipe.getBackingRecipe() != null) { + var type = node.recipe.getBackingRecipe().value().getType(); + if (type == RecipeType.CRAFTING || type == RecipeType.STONECUTTING || type == RecipeType.SMITHING) + isCraftingRecipe = true; + } + menu.clear(); + + if (isCraftingRecipe) { + PatternFillingHelper.encodeCraftingRecipe(menu, + node.recipe.getBackingRecipe(), + HandlerBoMRecipes.updateRecipe(node.recipe, BoM.tree.batches, + HandlerBoMRecipes.collectInputs(node)), + stack -> true); + + menu.encode(); + } else { + EmiStack nodeStack = node.ingredient.getEmiStacks().getFirst(); + List outputs = new ArrayList<>(node.recipe.getOutputs()); + outputs.remove(nodeStack); + outputs.addFirst(nodeStack); + PatternFillingHelper.encodeProcessingRecipe(menu, + HandlerBoMRecipes.updateRecipe(node.recipe, BoM.tree.batches, + HandlerBoMRecipes.collectInputs(node)), + outputs.stream().map(EmiStackHelper::toGenericStack).toList()); + + menu.encode(); + + String name = ExtendedAEPatternUploadUtil.mapRecipeTypeToSearchKey(node.recipe.getBackingRecipe().value()); + if (!(name == null || name.isBlank())) + ExtendedAEPatternUploadUtil.setLastProcessingName(name); + + PacketDistributor.sendToServer(RequestProvidersListC2SPacket.INSTANCE); + } + } + } + } + } catch (Exception ignored) {} + } +} diff --git a/src/main/java/com/extendedae_plus/network/C2SPacketStoneCuttingID.java b/src/main/java/com/extendedae_plus/network/C2SPacketStoneCuttingID.java new file mode 100644 index 0000000..b39f3a6 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/C2SPacketStoneCuttingID.java @@ -0,0 +1,31 @@ +package com.extendedae_plus.network; + +import appeng.menu.me.items.PatternEncodingTermMenu; +import com.extendedae_plus.ExtendedAEPlus; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public record C2SPacketStoneCuttingID(ResourceLocation recipeID) implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "bom_pattern_encoding")); + + @Override + public Type type() { + return TYPE; + } + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ResourceLocation.STREAM_CODEC, C2SPacketStoneCuttingID::recipeID, + C2SPacketStoneCuttingID::new); + + public static void handle(C2SPacketStoneCuttingID packet, IPayloadContext context) { + if (context.player().level().isClientSide) return; + context.enqueueWork(() -> { + if (!(context.player().containerMenu instanceof PatternEncodingTermMenu menu)) return; + menu.setStonecuttingRecipeId(packet.recipeID); + }); + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 7387f16..33dc99f 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -48,6 +48,7 @@ "ae2.client.gui.PatternProviderScreenMixin", "ae2.client.gui.SlotGridLayoutMixin", "ae2.menu.CraftConfirmMenuGoBackMixin", + "emi.MixinBoMScreen", "emi.MixinEncodingHelper", "extendedae.accessor.GuiExPatternTerminalAccessor", "extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",