From d4f57c85a30f8d980d6b5cfaf273ef07d8998938 Mon Sep 17 00:00:00 2001 From: GaLicn <3096147684@qq.com> Date: Fri, 15 Aug 2025 13:18:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0jei=E4=B8=ADshift=E5=B7=A6?= =?UTF-8?q?=E9=94=AE=E6=8B=89=E5=8F=96=E7=89=A9=E5=93=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../extendedae_plus/client/InputEvents.java | 57 +++++--- .../extendedae_plus/network/ModNetwork.java | 7 + .../network/PullFromJeiOrCraftC2SPacket.java | 136 ++++++++++++++++++ 4 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java diff --git a/gradle.properties b/gradle.properties index 62a0082..3513f82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G loom.platform = forge # Mod properties -mod_version = 1.2.1-fix +mod_version = 1.2.2 maven_group = com.extendedae_plus archives_name = extendedae_plus diff --git a/src/main/java/com/extendedae_plus/client/InputEvents.java b/src/main/java/com/extendedae_plus/client/InputEvents.java index b089ad6..8e7a58c 100644 --- a/src/main/java/com/extendedae_plus/client/InputEvents.java +++ b/src/main/java/com/extendedae_plus/client/InputEvents.java @@ -8,10 +8,12 @@ import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.integration.jei.JeiRuntimeProxy; import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.OpenCraftFromJeiC2SPacket; +import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket; import appeng.api.stacks.GenericStack; import appeng.integration.modules.jei.GenericEntryStackHelper; import mezz.jei.api.ingredients.ITypedIngredient; +import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.ScreenEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -23,26 +25,47 @@ public final class InputEvents { @SubscribeEvent public static void onMouseButtonPre(ScreenEvent.MouseButtonPressed.Pre event) { - // 只处理中键按下 - if (event.getButton() != GLFW.GLFW_MOUSE_BUTTON_MIDDLE) return; - - // 优先在 JEI 配方界面基于坐标获取;若无,再从覆盖层/书签获取 - double mouseX = event.getMouseX(); - double mouseY = event.getMouseY(); - Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY); - if (hovered.isEmpty()) { - hovered = JeiRuntimeProxy.getIngredientUnderMouse(); + // 优先处理:Shift + 左键(拉取或下单) + if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) { + double mouseX = event.getMouseX(); + double mouseY = event.getMouseY(); + Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY); + if (hovered.isEmpty()) { + hovered = JeiRuntimeProxy.getIngredientUnderMouse(); + } + if (hovered.isPresent()) { + ITypedIngredient typed = hovered.get(); + GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed); + if (stack != null) { + // 发送到服务端:若网络有库存则拉取一组到空槽,否则若可合成则打开下单界面 + ModNetwork.CHANNEL.sendToServer(new PullFromJeiOrCraftC2SPacket(stack)); + // 消费此次点击,避免 JEI/原版对左键的其它处理 + event.setCanceled(true); + return; + } + } } - if (hovered.isEmpty()) return; - ITypedIngredient typed = hovered.get(); - GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed); - if (stack == null) return; + // 中键:打开 AE 下单界面(保持原有功能) + if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_MIDDLE) { + // 优先在 JEI 配方界面基于坐标获取;若无,再从覆盖层/书签获取 + double mouseX = event.getMouseX(); + double mouseY = event.getMouseY(); + Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY); + if (hovered.isEmpty()) { + hovered = JeiRuntimeProxy.getIngredientUnderMouse(); + } + if (hovered.isEmpty()) return; - // 发送到服务端,让其验证并打开 CraftAmountMenu - ModNetwork.CHANNEL.sendToServer(new OpenCraftFromJeiC2SPacket(stack)); + ITypedIngredient typed = hovered.get(); + GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed); + if (stack == null) return; - // 消费此次点击,避免 JEI/原版对中键的其它处理 - event.setCanceled(true); + // 发送到服务端,让其验证并打开 CraftAmountMenu + ModNetwork.CHANNEL.sendToServer(new OpenCraftFromJeiC2SPacket(stack)); + + // 消费此次点击,避免 JEI/原版对中键的其它处理 + event.setCanceled(true); + } } } diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/network/ModNetwork.java index 4be045e..1923644 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -6,6 +6,7 @@ import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkDirection; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket; public class ModNetwork { private static final String PROTOCOL_VERSION = "1"; @@ -30,6 +31,12 @@ public class ModNetwork { .decoder(OpenCraftFromJeiC2SPacket::decode) .consumerNetworkThread(OpenCraftFromJeiC2SPacket::handle) .add(); + + CHANNEL.messageBuilder(PullFromJeiOrCraftC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(PullFromJeiOrCraftC2SPacket::encode) + .decoder(PullFromJeiOrCraftC2SPacket::decode) + .consumerNetworkThread(PullFromJeiOrCraftC2SPacket::handle) + .add(); } private static int nextId() { return id++; } diff --git a/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java b/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java new file mode 100644 index 0000000..e7e38e1 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java @@ -0,0 +1,136 @@ +package com.extendedae_plus.network; + +import java.util.function.Supplier; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.network.NetworkEvent; +import net.minecraft.network.FriendlyByteBuf; + +import appeng.api.networking.IGrid; +import appeng.api.storage.MEStorage; +import appeng.api.storage.StorageHelper; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.api.networking.energy.IEnergyService; +import appeng.items.tools.powered.WirelessCraftingTerminalItem; +import appeng.items.tools.powered.WirelessTerminalItem; +import appeng.me.helpers.PlayerSource; +import appeng.menu.locator.MenuLocators; +import appeng.menu.me.crafting.CraftAmountMenu; + +// ae2wtlib +import de.mari_023.ae2wtlib.wut.WUTHandler; +import de.mari_023.ae2wtlib.wut.WTDefinition; +import de.mari_023.ae2wtlib.terminal.WTMenuHost; + +import com.extendedae_plus.util.WirelessTerminalLocator; +import com.extendedae_plus.util.WirelessTerminalLocator.LocatedTerminal; +import com.extendedae_plus.menu.locator.CuriosItemLocator; + +public class PullFromJeiOrCraftC2SPacket { + private final GenericStack stack; + + public PullFromJeiOrCraftC2SPacket(GenericStack stack) { + this.stack = stack; + } + + public static void encode(PullFromJeiOrCraftC2SPacket msg, FriendlyByteBuf buf) { + GenericStack.writeBuffer(msg.stack, buf); + } + + public static PullFromJeiOrCraftC2SPacket decode(FriendlyByteBuf buf) { + var gs = GenericStack.readBuffer(buf); + return new PullFromJeiOrCraftC2SPacket(gs); + } + + public static void handle(PullFromJeiOrCraftC2SPacket msg, Supplier ctx) { + NetworkEvent.Context context = ctx.get(); + context.enqueueWork(() -> { + ServerPlayer player = context.getSender(); + if (player == null || msg.stack == null) return; + + // 仅处理物品 + AEKey what = msg.stack.what(); + if (!(what instanceof AEItemKey itemKey)) return; + + // 定位玩家持有/Curios 的无线终端 + LocatedTerminal located = WirelessTerminalLocator.find(player); + ItemStack terminal = located.stack; + if (terminal.isEmpty()) return; + + IGrid grid; + boolean usedWtHost = false; + // Curios 情况优先通过 WTMenuHost 获取网络并由其处理能量 + String curiosSlotId = located.getCuriosSlotId(); + int curiosIndex = located.getCuriosIndex(); + WTMenuHost wtHost = null; + if (curiosSlotId != null && curiosIndex >= 0) { + String current = WUTHandler.getCurrentTerminal(terminal); + WTDefinition def = WUTHandler.wirelessTerminals.get(current); + if (def == null) return; + wtHost = def.wTMenuHostFactory().create(player, null, terminal, (p, sub) -> {}); + if (wtHost == null) return; + var node = wtHost.getActionableNode(); + if (node == null) return; + grid = node.getGrid(); + if (grid == null) return; + if (!wtHost.drainPower()) return; + usedWtHost = true; + } else { + // 原生路径 + ServerLevel level = player.serverLevel(); + WirelessCraftingTerminalItem wct = terminal.getItem() instanceof WirelessCraftingTerminalItem c ? c : null; + WirelessTerminalItem wt = wct != null ? wct : (terminal.getItem() instanceof WirelessTerminalItem t ? t : null); + if (wt == null) return; + grid = wt.getLinkedGrid(terminal, level, player); + if (grid == null) return; + if (!wt.hasPower(player, 0.5, terminal)) return; + } + + // 仅放入背包空槽位 + var inv = player.getInventory(); + int free = inv.getFreeSlot(); + if (free == -1) return; // 背包已满 + + int targetMax = itemKey.toStack(1).getMaxStackSize(); + IEnergyService energy = grid.getEnergyService(); + MEStorage storage = grid.getStorageService().getInventory(); + + long extracted = StorageHelper.poweredExtraction(energy, storage, itemKey, targetMax, new PlayerSource(player)); + if (extracted > 0) { + inv.setItem(free, itemKey.toStack((int) extracted)); + if (!usedWtHost) { + // 扣能:与 PickFromWirelessC2SPacket 保持一致 + WirelessCraftingTerminalItem wct2 = terminal.getItem() instanceof WirelessCraftingTerminalItem c2 ? c2 : null; + WirelessTerminalItem wt2 = wct2 != null ? wct2 : (terminal.getItem() instanceof WirelessTerminalItem t2 ? t2 : null); + if (wt2 != null) { + wt2.usePower(player, Math.max(0.5, extracted * 0.05), terminal); + } + } + located.commit(); + player.containerMenu.broadcastChanges(); + return; + } + + // 无库存时:若可合成则打开下单界面 + var craftingService = grid.getCraftingService(); + if (!craftingService.isCraftable(what)) return; + + if (curiosSlotId != null && curiosIndex >= 0) { + CraftAmountMenu.open(player, new CuriosItemLocator(curiosSlotId, curiosIndex), what, 1); + } else { + var hand = located.getHand(); + int slot = located.getSlotIndex(); + if (hand != null) { + CraftAmountMenu.open(player, MenuLocators.forHand(player, hand), what, 1); + } else if (slot >= 0) { + CraftAmountMenu.open(player, MenuLocators.forInventorySlot(slot), what, 1); + } + } + }); + context.setPacketHandled(true); + } +}