增加jei中shift左键拉取物品功能

This commit is contained in:
GaLicn 2025-08-15 13:18:12 +08:00
parent c325cc1d39
commit d4f57c85a3
4 changed files with 184 additions and 18 deletions

View File

@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G
loom.platform = forge loom.platform = forge
# Mod properties # Mod properties
mod_version = 1.2.1-fix mod_version = 1.2.2
maven_group = com.extendedae_plus maven_group = com.extendedae_plus
archives_name = extendedae_plus archives_name = extendedae_plus

View File

@ -8,10 +8,12 @@ import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.integration.jei.JeiRuntimeProxy; import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.ModNetwork;
import com.extendedae_plus.network.OpenCraftFromJeiC2SPacket; import com.extendedae_plus.network.OpenCraftFromJeiC2SPacket;
import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket;
import appeng.api.stacks.GenericStack; import appeng.api.stacks.GenericStack;
import appeng.integration.modules.jei.GenericEntryStackHelper; import appeng.integration.modules.jei.GenericEntryStackHelper;
import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.ingredients.ITypedIngredient;
import net.minecraft.client.gui.screens.Screen;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ScreenEvent; import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
@ -23,26 +25,47 @@ public final class InputEvents {
@SubscribeEvent @SubscribeEvent
public static void onMouseButtonPre(ScreenEvent.MouseButtonPressed.Pre event) { public static void onMouseButtonPre(ScreenEvent.MouseButtonPressed.Pre event) {
// 只处理中键按下 // 优先处理Shift + 左键拉取或下单
if (event.getButton() != GLFW.GLFW_MOUSE_BUTTON_MIDDLE) return; if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) {
double mouseX = event.getMouseX();
// 优先在 JEI 配方界面基于坐标获取若无再从覆盖层/书签获取 double mouseY = event.getMouseY();
double mouseX = event.getMouseX(); Optional<ITypedIngredient<?>> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY);
double mouseY = event.getMouseY(); if (hovered.isEmpty()) {
Optional<ITypedIngredient<?>> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY); hovered = JeiRuntimeProxy.getIngredientUnderMouse();
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(); // 中键打开 AE 下单界面保持原有功能
GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed); if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_MIDDLE) {
if (stack == null) return; // 优先在 JEI 配方界面基于坐标获取若无再从覆盖层/书签获取
double mouseX = event.getMouseX();
double mouseY = event.getMouseY();
Optional<ITypedIngredient<?>> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY);
if (hovered.isEmpty()) {
hovered = JeiRuntimeProxy.getIngredientUnderMouse();
}
if (hovered.isEmpty()) return;
// 发送到服务端让其验证并打开 CraftAmountMenu ITypedIngredient<?> typed = hovered.get();
ModNetwork.CHANNEL.sendToServer(new OpenCraftFromJeiC2SPacket(stack)); GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed);
if (stack == null) return;
// 消费此次点击避免 JEI/原版对中键的其它处理 // 发送到服务端让其验证并打开 CraftAmountMenu
event.setCanceled(true); ModNetwork.CHANNEL.sendToServer(new OpenCraftFromJeiC2SPacket(stack));
// 消费此次点击避免 JEI/原版对中键的其它处理
event.setCanceled(true);
}
} }
} }

View File

@ -6,6 +6,7 @@ import net.minecraftforge.network.simple.SimpleChannel;
import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkDirection;
import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket;
public class ModNetwork { public class ModNetwork {
private static final String PROTOCOL_VERSION = "1"; private static final String PROTOCOL_VERSION = "1";
@ -30,6 +31,12 @@ public class ModNetwork {
.decoder(OpenCraftFromJeiC2SPacket::decode) .decoder(OpenCraftFromJeiC2SPacket::decode)
.consumerNetworkThread(OpenCraftFromJeiC2SPacket::handle) .consumerNetworkThread(OpenCraftFromJeiC2SPacket::handle)
.add(); .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++; } private static int nextId() { return id++; }

View File

@ -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<NetworkEvent.Context> 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);
}
}