diff --git a/src/main/java/com/extendedae_plus/menu/locator/CuriosItemLocator.java b/src/main/java/com/extendedae_plus/menu/locator/CuriosItemLocator.java index 4b00cab..f0902c6 100644 --- a/src/main/java/com/extendedae_plus/menu/locator/CuriosItemLocator.java +++ b/src/main/java/com/extendedae_plus/menu/locator/CuriosItemLocator.java @@ -5,7 +5,7 @@ import appeng.api.implementations.menuobjects.ItemMenuHost; import appeng.helpers.WirelessTerminalMenuHost; import appeng.items.tools.powered.WirelessTerminalItem; import appeng.menu.MenuOpener; -import appeng.menu.locator.MenuLocator; +import appeng.menu.locator.MenuHostLocator; import appeng.menu.me.common.MEStorageMenu; import com.extendedae_plus.menu.host.CuriosWTMenuHost; import com.extendedae_plus.menu.host.CuriosWirelessTerminalMenuHost; @@ -23,7 +23,7 @@ import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler; * 适配 Curios 槽位的自定义 MenuLocator: * 通过 slotId + index 在两端查找 Curios 实际物品引用,确保 NBT 变化(如耗电)能持久化。 */ -public record CuriosItemLocator(String slotId, int index) implements MenuLocator { +public record CuriosItemLocator(String slotId, int index) implements MenuHostLocator { @Override @Nullable public T locate(Player player, Class hostInterface) { diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/network/ModNetwork.java index c16d7f9..b8ff570 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -20,5 +20,9 @@ public class ModNetwork { registrar.playToServer(CraftingMonitorOpenProviderC2SPacket.TYPE, CraftingMonitorOpenProviderC2SPacket.STREAM_CODEC, CraftingMonitorOpenProviderC2SPacket::handle); registrar.playToServer(OpenProviderUiC2SPacket.TYPE, OpenProviderUiC2SPacket.STREAM_CODEC, OpenProviderUiC2SPacket::handle); registrar.playToServer(UploadEncodedPatternToProviderC2SPacket.TYPE, UploadEncodedPatternToProviderC2SPacket.STREAM_CODEC, UploadEncodedPatternToProviderC2SPacket::handle); + // 新增:JEI 中键打开合成界面 & 无线终端拾取方块物品 + registrar.playToServer(OpenCraftFromJeiC2SPacket.TYPE, OpenCraftFromJeiC2SPacket.STREAM_CODEC, OpenCraftFromJeiC2SPacket::handle); + registrar.playToServer(PickFromWirelessC2SPacket.TYPE, PickFromWirelessC2SPacket.STREAM_CODEC, PickFromWirelessC2SPacket::handle); + registrar.playToServer(PullFromJeiOrCraftC2SPacket.TYPE, PullFromJeiOrCraftC2SPacket.STREAM_CODEC, PullFromJeiOrCraftC2SPacket::handle); } } diff --git a/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java b/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java index 5a9a33d..5cefd91 100644 --- a/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java @@ -8,36 +8,39 @@ import appeng.menu.locator.MenuLocators; import appeng.menu.me.crafting.CraftAmountMenu; import com.extendedae_plus.menu.locator.CuriosItemLocator; import com.extendedae_plus.util.WirelessTerminalLocator; -import net.minecraft.network.FriendlyByteBuf; +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.minecraft.server.level.ServerPlayer; -import net.minecraftforge.network.NetworkEvent; - -import java.util.function.Supplier; +import net.neoforged.neoforge.network.handling.IPayloadContext; /** * C2S:从 JEI 中键点击请求打开 AE 的下单界面。 * 负载为一个 GenericStack(物品或流体)。 */ -public class OpenCraftFromJeiC2SPacket { +public class OpenCraftFromJeiC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "open_craft_from_jei")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> GenericStack.writeBuffer(pkt.stack, buf), + buf -> new OpenCraftFromJeiC2SPacket(GenericStack.readBuffer(buf)) + ); private final GenericStack stack; public OpenCraftFromJeiC2SPacket(GenericStack stack) { this.stack = stack; } - public static void encode(OpenCraftFromJeiC2SPacket msg, FriendlyByteBuf buf) { - GenericStack.writeBuffer(msg.stack, buf); + @Override + public Type type() { + return TYPE; } - public static OpenCraftFromJeiC2SPacket decode(FriendlyByteBuf buf) { - var gs = GenericStack.readBuffer(buf); - return new OpenCraftFromJeiC2SPacket(gs); - } - - public static void handle(OpenCraftFromJeiC2SPacket msg, Supplier ctx) { - var context = ctx.get(); - context.enqueueWork(() -> { - ServerPlayer player = context.getSender(); + public static void handle(final OpenCraftFromJeiC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; if (player == null || msg.stack == null) return; // 仅支持 AEKey 为可合成的种类 @@ -61,7 +64,7 @@ public class OpenCraftFromJeiC2SPacket { if (!(located.stack.getItem() instanceof WirelessTerminalItem wt)) return; // 基本前置校验:联网、电量 - IGrid grid = wt.getLinkedGrid(located.stack, player.level(), player); + IGrid grid = wt.getLinkedGrid(located.stack, player.level(), null); if (grid == null) return; if (!wt.hasPower(player, 0.5, located.stack)) return; @@ -82,6 +85,5 @@ public class OpenCraftFromJeiC2SPacket { // 未知宿主(回退忽略) } }); - context.setPacketHandled(true); } } diff --git a/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java b/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java index e55878a..5c0a52e 100644 --- a/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java @@ -10,23 +10,39 @@ import appeng.items.tools.powered.WirelessTerminalItem; import appeng.me.helpers.PlayerSource; import com.extendedae_plus.util.WirelessTerminalLocator; import com.extendedae_plus.util.WirelessTerminalLocator.LocatedTerminal; -import de.mari_023.ae2wtlib.terminal.WTMenuHost; -import de.mari_023.ae2wtlib.wut.WTDefinition; -import de.mari_023.ae2wtlib.wut.WUTHandler; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.network.FriendlyByteBuf; +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.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.network.NetworkEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; -import java.util.function.Supplier; +public class PickFromWirelessC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "pick_from_wireless")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> { + buf.writeBlockPos(pkt.pos); + buf.writeEnum(pkt.face); + buf.writeDouble(pkt.hitLoc.x); + buf.writeDouble(pkt.hitLoc.y); + buf.writeDouble(pkt.hitLoc.z); + }, + buf -> new PickFromWirelessC2SPacket( + buf.readBlockPos(), + buf.readEnum(Direction.class), + new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble()) + ) + ); -public class PickFromWirelessC2SPacket { private final BlockPos pos; private final Direction face; private final Vec3 hitLoc; @@ -37,101 +53,41 @@ public class PickFromWirelessC2SPacket { this.hitLoc = hitLoc; } - public static void encode(PickFromWirelessC2SPacket msg, FriendlyByteBuf buf) { - buf.writeBlockPos(msg.pos); - buf.writeEnum(msg.face); - buf.writeDouble(msg.hitLoc.x); - buf.writeDouble(msg.hitLoc.y); - buf.writeDouble(msg.hitLoc.z); + @Override + public Type type() { + return TYPE; } - public static PickFromWirelessC2SPacket decode(FriendlyByteBuf buf) { - BlockPos pos = buf.readBlockPos(); - Direction face = buf.readEnum(Direction.class); - double x = buf.readDouble(); - double y = buf.readDouble(); - double z = buf.readDouble(); - return new PickFromWirelessC2SPacket(pos, face, new Vec3(x, y, z)); - } + public static void handle(final PickFromWirelessC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; + if (player.isCreative()) return; - public static void handle(PickFromWirelessC2SPacket msg, Supplier ctx) { - NetworkEvent.Context context = ctx.get(); - context.enqueueWork(() -> { - ServerPlayer player = context.getSender(); - if (player == null || player.isCreative()) { - return; - } ServerLevel level = player.serverLevel(); BlockState state = level.getBlockState(msg.pos); - if (state == null || state.isAir()) { - return; - } + if (state == null || state.isAir()) return; // 服务端权威:定位玩家任意槽位的无线终端(含 Curios) LocatedTerminal located = WirelessTerminalLocator.find(player); ItemStack terminal = located.stack; - if (terminal.isEmpty()) { - return; - } + if (terminal.isEmpty()) return; IGrid grid; - boolean usedWtHost = false; - // 若来自 Curios:优先通过 ae2wtlib 的 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) { - wtHost = def.wTMenuHostFactory().create(player, null, terminal, (p, sub) -> {}); - if (wtHost != null) { - var node = wtHost.getActionableNode(); - if (node != null) { - grid = node.getGrid(); - if (grid == null) { - return; - } - // 通过 WTMenuHost 的电力处理以兼容量子卡补能 - if (!wtHost.drainPower()) { - return; - } - usedWtHost = true; - } else { - return; - } - } else { - return; - } - } else { - return; - } - } else { - // 非 Curios:按 AE2 原生路径处理 - 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; - } - } + // 统一走 AE2 原生路径处理(包含 Curios 情况): + 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, null); + if (grid == null) return; + if (!wt.hasPower(player, 0.5, terminal)) return; - // 计算 pick 对应的物品:使用客户端实际命中位置,保证多部件方块(AE2 CableBus/部件)能返回正确克隆物品 + // 计算 pick 对应的物品:使用客户端实际命中位置,保证多部件方块能返回正确克隆物品 BlockHitResult bhr = new BlockHitResult(msg.hitLoc, msg.face, msg.pos, true); ItemStack picked = state.getBlock().getCloneItemStack(state, bhr, level, msg.pos, player); if (picked.isEmpty()) { - // 兜底用方块本身 picked = state.getBlock().asItem().getDefaultInstance(); } - if (picked.isEmpty()) { - return; - } + if (picked.isEmpty()) return; int targetMax = picked.getMaxStackSize(); AEItemKey targetKey = AEItemKey.of(picked); @@ -142,10 +98,6 @@ public class PickFromWirelessC2SPacket { ItemStack inHand = player.getMainHandItem(); var inv = player.getInventory(); - // 决定放置目标: - // 1) 若主手为空 -> 放主手,空间为整组 - // 2) 若主手为同一物品且未满 -> 合并到主手,空间为主手剩余空间 - // 3) 其他情况(主手不为空且不是同物品)-> 放入背包空槽,空间为整组 boolean handIsSameItem = !inHand.isEmpty() && AEItemKey.of(inHand).equals(targetKey); boolean placeToMainHand = inHand.isEmpty() || (handIsSameItem && inHand.getCount() < inHand.getMaxStackSize()); @@ -154,56 +106,41 @@ public class PickFromWirelessC2SPacket { space = inHand.isEmpty() ? targetMax : Math.min(targetMax, inHand.getMaxStackSize() - inHand.getCount()); } else { int free = inv.getFreeSlot(); - if (free == -1) { - return; // 背包已满,不进行拉取 - } + if (free == -1) return; space = targetMax; } - if (space <= 0) { - return; - } + if (space <= 0) return; long extracted = StorageHelper.poweredExtraction(energy, storage, targetKey, space, new PlayerSource(player)); - if (extracted <= 0) { - return; - } + if (extracted <= 0) return; if (placeToMainHand) { if (inHand.isEmpty()) { inv.setItem(inv.selected, targetKey.toStack((int) extracted)); } else { - // 合并到主手 int add = (int) Math.min(extracted, inHand.getMaxStackSize() - inHand.getCount()); if (add > 0) { inHand.grow(add); - inv.setItem(inv.selected, inHand); // 写回以确保同步 + inv.setItem(inv.selected, inHand); } } } else { int free = inv.getFreeSlot(); if (free == -1) { - // 理论上不会发生(上面已判断),为安全起见:将提取物退回网络 StorageHelper.poweredInsert(energy, storage, targetKey, extracted, new PlayerSource(player)); return; } inv.setItem(free, targetKey.toStack((int) extracted)); } - if (usedWtHost) { - // WTMenuHost 已在 drainPower 中处理能量消耗/回充,此处不重复扣除 - } else { - // 原生 AE2 扣能 - 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); - } + 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); } - // 确保写回(若位于 Curios 等需要显式写回的容器) located.commit(); player.containerMenu.broadcastChanges(); }); - context.setPacketHandled(true); } } diff --git a/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java b/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java index ee05ae0..7a81435 100644 --- a/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java @@ -15,81 +15,60 @@ import appeng.menu.me.crafting.CraftAmountMenu; import com.extendedae_plus.menu.locator.CuriosItemLocator; import com.extendedae_plus.util.WirelessTerminalLocator; import com.extendedae_plus.util.WirelessTerminalLocator.LocatedTerminal; -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.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; 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.neoforged.neoforge.network.handling.IPayloadContext; -import java.util.function.Supplier; +public class PullFromJeiOrCraftC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "pull_from_jei_or_craft")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> GenericStack.writeBuffer(pkt.stack, buf), + buf -> new PullFromJeiOrCraftC2SPacket(GenericStack.readBuffer(buf)) + ); -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); + @Override + public Type type() { + return TYPE; } - public static PullFromJeiOrCraftC2SPacket decode(FriendlyByteBuf buf) { - var gs = GenericStack.readBuffer(buf); - return new PullFromJeiOrCraftC2SPacket(gs); - } + public static void handle(final PullFromJeiOrCraftC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; + if (msg.stack == null) return; - 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; - } + // 统一 AE2 原生路径 + 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, null); + if (grid == null) return; + if (!wt.hasPower(player, 0.5, terminal)) return; - // 仅放入背包空槽位 var inv = player.getInventory(); int free = inv.getFreeSlot(); - if (free == -1) return; // 背包已满 + if (free == -1) return; int targetMax = itemKey.toStack(1).getMaxStackSize(); IEnergyService energy = grid.getEnergyService(); @@ -98,23 +77,21 @@ public class PullFromJeiOrCraftC2SPacket { 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); - } + 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; + String curiosSlotId = located.getCuriosSlotId(); + int curiosIndex = located.getCuriosIndex(); if (curiosSlotId != null && curiosIndex >= 0) { CraftAmountMenu.open(player, new CuriosItemLocator(curiosSlotId, curiosIndex), what, 1); } else { @@ -127,6 +104,5 @@ public class PullFromJeiOrCraftC2SPacket { } } }); - context.setPacketHandled(true); } } diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java index 9876423..c49cf6f 100644 --- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java @@ -296,7 +296,6 @@ public class ExtendedAEPatternUploadUtil { /** * 仅使用反射的 GTCEu GTRecipe -> 搜索关键字(避免在运行时直接引用 GTCEu 类)。 - * 逻辑与 {@link #mapGTCEuRecipeToSearchKey(com.gregtechceu.gtceu.api.recipe.GTRecipe)} 等价。 */ public static String mapGTCEuRecipeToSearchKey(Object gtRecipeObj) { if (gtRecipeObj == null) return null;