diff --git a/src/main/java/com/extendedae_plus/api/upload/IPatternEncodingIdSync.java b/src/main/java/com/extendedae_plus/api/upload/IPatternEncodingIdSync.java new file mode 100644 index 0000000..13a73ad --- /dev/null +++ b/src/main/java/com/extendedae_plus/api/upload/IPatternEncodingIdSync.java @@ -0,0 +1,10 @@ +package com.extendedae_plus.api.upload; + +import net.minecraft.resources.ResourceLocation; + +public interface IPatternEncodingIdSync { + /** + * 由客户端发送的编码指令附带的 recipe id。 + */ + void eap$clientRecipeIdUpload(ResourceLocation id); +} diff --git a/src/main/java/com/extendedae_plus/init/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java index 31534f0..acdc286 100644 --- a/src/main/java/com/extendedae_plus/init/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -3,6 +3,7 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.network.*; import com.extendedae_plus.network.packet.EAPConfigButtonPacket; +import com.extendedae_plus.network.upload.EncodeWithRecipeIdC2SPacket; import com.extendedae_plus.network.upload.EncodeWithShiftFlagC2SPacket; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; @@ -26,6 +27,7 @@ public class ModNetwork { registrar.playToServer(CreateAndUploadPatternC2SPacket.TYPE, CreateAndUploadPatternC2SPacket.STREAM_CODEC, CreateAndUploadPatternC2SPacket::handle); registrar.playToServer(CancelPendingPatternC2SPacket.TYPE,CancelPendingPatternC2SPacket.STREAM_CODEC,CancelPendingPatternC2SPacket::handle); registrar.playToServer(EncodeWithShiftFlagC2SPacket.TYPE, EncodeWithShiftFlagC2SPacket.STREAM_CODEC, EncodeWithShiftFlagC2SPacket::handle); + registrar.playToServer(EncodeWithRecipeIdC2SPacket.TYPE, EncodeWithRecipeIdC2SPacket.STREAM_CODEC, EncodeWithRecipeIdC2SPacket::handle); // 新增:JEI 中键打开合成界面 & 无线终端拾取方块物品 registrar.playToServer(com.extendedae_plus.network.OpenCraftFromJeiC2SPacket.TYPE, com.extendedae_plus.network.OpenCraftFromJeiC2SPacket.STREAM_CODEC, diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java index 619368d..106ffd3 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java @@ -5,6 +5,7 @@ import com.extendedae_plus.config.ModConfigs; import net.minecraft.ChatFormatting; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; @@ -28,6 +29,12 @@ public class EncodedPatternItemMixin { String name = tag.getString("encodePlayer"); lines.add(Component.translatable("extendedae_plus.pattern.hovertext.player", name).withStyle(ChatFormatting.GRAY)); } + if (tag.contains("recipeId")) { + String id = tag.getString("recipeId"); + ResourceLocation location = ResourceLocation.tryParse(id); + if (location == null) return; + lines.add(Component.translatable("extendedae_plus.pattern.hovertext.player", Component.translatable("recipe." + location.getNamespace() + "." + location.getPath())).withStyle(ChatFormatting.GRAY)); + } } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java index e8036e9..9ff46b2 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java @@ -1,14 +1,20 @@ package com.extendedae_plus.mixin.ae2.menu; import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.stacks.GenericStack; import appeng.menu.me.items.PatternEncodingTermMenu; import appeng.menu.slot.RestrictedInputSlot; import appeng.parts.encoding.EncodingMode; +import com.extendedae_plus.api.upload.IPatternEncodingIdSync; import com.extendedae_plus.api.upload.IPatternEncodingShiftUploadSync; +import com.extendedae_plus.util.RecipeFinderUtilEMI; import com.extendedae_plus.util.uploadPattern.ExtendedAEPatternUploadUtil; import com.glodblock.github.glodium.network.packet.sync.ActionMap; import com.glodblock.github.glodium.network.packet.sync.IActionHolder; +import dev.emi.emi.api.recipe.EmiRecipe; +import dev.emi.emi.api.stack.EmiStack; import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -22,13 +28,14 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.List; + /** * 给 AE2 的 PatternEncodingTermMenu 增加一个通用动作持有者,实现接收 EPP 的 CGenericPacket 动作。 * 注册动作 "upload_to_matrix":仅上传“合成图样”到 ExtendedAE 装配矩阵。 */ @Mixin(PatternEncodingTermMenu.class) -public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHolder, IPatternEncodingShiftUploadSync { - +public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHolder, IPatternEncodingShiftUploadSync, IPatternEncodingIdSync { @Unique private final ActionMap eap$actions = ActionMap.create(); @@ -38,6 +45,9 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo @Unique private boolean eap$pendingShiftUpload; + @Unique + private ResourceLocation eap$pendingRecipeIdUpload; + @Shadow(remap = false) private RestrictedInputSlot encodedPatternSlot; @@ -55,6 +65,11 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo return flag; } + @Unique + @Override + public void eap$clientRecipeIdUpload(ResourceLocation id) { + this.eap$pendingRecipeIdUpload = id; + } @Unique private void eap$scheduleUploadWithRetry(ServerPlayer sp, PatternEncodingTermMenu menu, int attemptsLeft) { sp.server.execute(() -> { @@ -140,7 +155,24 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo if (itemStack != null && !itemStack.isEmpty()) { CustomData.update(DataComponents.CUSTOM_DATA, itemStack, tag -> { tag.putString("encodePlayer", this.epp$player.getGameProfile().getName()); + if (this.eap$pendingRecipeIdUpload !=null) { + EmiRecipe recipe = RecipeFinderUtilEMI.findRecipeById(this.eap$pendingRecipeIdUpload); + // 检查样板与配方是否对应 + List stacks = PatternDetailsHelper.decodePattern(itemStack, this.epp$player.level()).getOutputs(); + if (stacks != null && recipe != null) { + List stacks2 = recipe.getOutputs(); + + List ids1 = stacks.stream().map(s -> s.what().getId()).sorted().toList(); + List ids2 = stacks2.stream().map(EmiStack::getId).sorted().toList(); + + if (ids1.equals(ids2)) { + tag.putString("recipeId", this.eap$pendingRecipeIdUpload.toString()); + } + } + this.eap$pendingRecipeIdUpload =null; + } }); + cir.setReturnValue(itemStack); } } diff --git a/src/main/java/com/extendedae_plus/mixin/emi/EmiRecipeFillerMixin.java b/src/main/java/com/extendedae_plus/mixin/emi/EmiRecipeFillerMixin.java new file mode 100644 index 0000000..8cac166 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/emi/EmiRecipeFillerMixin.java @@ -0,0 +1,21 @@ +package com.extendedae_plus.mixin.emi; + +import com.extendedae_plus.network.upload.EncodeWithRecipeIdC2SPacket; +import dev.emi.emi.api.recipe.EmiRecipe; +import dev.emi.emi.api.recipe.handler.EmiCraftContext; +import dev.emi.emi.registry.EmiRecipeFiller; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.neoforged.neoforge.network.PacketDistributor; +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; + +@Mixin(EmiRecipeFiller.class) +public class EmiRecipeFillerMixin { + @Inject(method = "performFill", at = @At("RETURN"), remap = false) + private static void onPerformFill(EmiRecipe recipe, AbstractContainerScreen screen, EmiCraftContext.Type type, EmiCraftContext.Destination destination, int amount, CallbackInfoReturnable cir) { + PacketDistributor.sendToServer(new EncodeWithRecipeIdC2SPacket(recipe.getId())); + } +} diff --git a/src/main/java/com/extendedae_plus/network/upload/EncodeWithRecipeIdC2SPacket.java b/src/main/java/com/extendedae_plus/network/upload/EncodeWithRecipeIdC2SPacket.java new file mode 100644 index 0000000..7803f64 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/upload/EncodeWithRecipeIdC2SPacket.java @@ -0,0 +1,49 @@ +package com.extendedae_plus.network.upload; + +import appeng.menu.me.items.PatternEncodingTermMenu; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.api.upload.IPatternEncodingIdSync; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +/** + * C2S:图样编码终端点击「编码」按钮时上传客户端的配方ID。 + */ +public class EncodeWithRecipeIdC2SPacket implements CustomPacketPayload { + + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "encode_with_recipe")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> buf.writeResourceLocation(pkt.id), + buf -> new EncodeWithRecipeIdC2SPacket(buf.readResourceLocation()) + ); + + private final ResourceLocation id; + + public EncodeWithRecipeIdC2SPacket(ResourceLocation id) { + this.id = id; + } + + public static void handle(final EncodeWithRecipeIdC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) { + return; + } + if (player.containerMenu instanceof PatternEncodingTermMenu menu + && menu instanceof IPatternEncodingIdSync sync) { + sync.eap$clientRecipeIdUpload(msg.id); + } + }); + } + + @Override + public Type type() { + return TYPE; + } + +} diff --git a/src/main/java/com/extendedae_plus/util/RecipeFinderUtilEMI.java b/src/main/java/com/extendedae_plus/util/RecipeFinderUtilEMI.java new file mode 100644 index 0000000..783e6a4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/RecipeFinderUtilEMI.java @@ -0,0 +1,52 @@ +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 dev.emi.emi.api.EmiApi; +import dev.emi.emi.api.recipe.EmiRecipe; +import dev.emi.emi.api.recipe.EmiRecipeManager; +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.neoforge.NeoForgeTypes; +import mezz.jei.api.recipe.*; +import mezz.jei.api.recipe.category.IRecipeCategory; +import mezz.jei.api.runtime.IJeiRuntime; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CraftingRecipe; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.neoforged.neoforge.fluids.FluidStack; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * 基于 EMI 查找配方,提取输入输出槽位与数量信息。 + */ +public final class RecipeFinderUtilEMI { + private static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAE Plus - RecipeFinder"); + static EmiRecipeManager manager = EmiApi.getRecipeManager(); + private RecipeFinderUtilEMI() { + } + + @Nullable + public static EmiRecipe findRecipeById(ResourceLocation location) { + return manager.getRecipe(location); + } + @Nullable + public static EmiRecipe findRecipeById(String id) { + return findRecipeById(ResourceLocation.parse(id)); + } + +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index a36ae72..afe174a 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -53,10 +53,10 @@ "ae2.parts.automation.IOBusPartChannelCardMixin", "ae2.parts.storagebus.StorageBusPartChannelCardMixin", "ae2WTlib.ContainerUWirelessExPatternTerminalMixin", + "emi.EmiRecipeFillerMixin", "extendedae.common.PartExPatternProviderMixin", "extendedae.common.TileExPatternProviderMixin", "extendedae.common.matrix.ClusterAssemblerMatrixMixin", - "extendedae.common.matrix.ClusterAssemblerMatrixMixin", "extendedae.container.ContainerAssemblerMatrixPatternSlotTrackerMixin", "extendedae.container.ContainerExPatternProviderMixin", "extendedae.container.ContainerExPatternTerminalMixin",