From 8e74d13d73bb29309071b578a82748244f4ef445 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sat, 20 Sep 2025 19:37:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcurios=E4=B8=AD=E6=97=A0?= =?UTF-8?q?=E7=BA=BF=E7=BB=88=E7=AB=AF=E7=9B=B8=E5=85=B3=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/extendedae_plus/ExtendedAEPlus.java | 12 ++ .../menu/host/CuriosWTSubMenuHost.java | 21 +++ .../menu/host/CuriosWTSubMenuHost2.java | 21 +++ .../menu/locator/CuriosItemLocator.java | 124 +++++++++++++++--- .../network/OpenCraftFromJeiC2SPacket.java | 41 +++++- .../network/PickFromWirelessC2SPacket.java | 24 ++-- .../network/PullFromJeiOrCraftC2SPacket.java | 9 ++ 7 files changed, 219 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost.java create mode 100644 src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost2.java diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 75c26a7..5a6277c 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -79,6 +79,18 @@ public class ExtendedAEPlus { // 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空 event.enqueueWork(() -> { try { + // 注册自定义 AE2 MenuLocator(用于 Curios 槽位打开菜单) + try { + appeng.menu.locator.MenuLocators.register( + com.extendedae_plus.menu.locator.CuriosItemLocator.class, + com.extendedae_plus.menu.locator.CuriosItemLocator::writeToPacket, + com.extendedae_plus.menu.locator.CuriosItemLocator::readFromPacket + ); + LOGGER.info("Registered AE2 MenuLocator: CuriosItemLocator"); + } catch (Throwable t) { + LOGGER.warn("Failed to register CuriosItemLocator with AE2 MenuLocators: {}", t.toString()); + } + AEBaseEntityBlock b4 = (AEBaseEntityBlock) ModBlocks.ACCELERATOR_4x.get(); AEBaseEntityBlock b16 = (AEBaseEntityBlock) ModBlocks.ACCELERATOR_16x.get(); AEBaseEntityBlock b64 = (AEBaseEntityBlock) ModBlocks.ACCELERATOR_64x.get(); diff --git a/src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost.java b/src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost.java new file mode 100644 index 0000000..3ff3bff --- /dev/null +++ b/src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost.java @@ -0,0 +1,21 @@ +package com.extendedae_plus.menu.host; + +import appeng.menu.ISubMenu; +import appeng.menu.locator.ItemMenuHostLocator; +import appeng.api.storage.ISubMenuHost; +import de.mari_023.ae2wtlib.api.terminal.ItemWT; +import de.mari_023.ae2wtlib.api.terminal.WTMenuHost; +import net.minecraft.world.entity.player.Player; + +/** + * 适配 ae2wtlib 的 WTMenuHost 到 AE2 期望的 ISubMenuHost。 + * 仅作为类型桥接,具体行为由 WTMenuHost 超类实现。 + */ +public class CuriosWTSubMenuHost extends WTMenuHost implements ISubMenuHost { + public CuriosWTSubMenuHost(ItemWT item, + Player player, + ItemMenuHostLocator locator, + java.util.function.BiConsumer returnToMainMenu) { + super(item, player, locator, returnToMainMenu); + } +} diff --git a/src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost2.java b/src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost2.java new file mode 100644 index 0000000..8d2b9bf --- /dev/null +++ b/src/main/java/com/extendedae_plus/menu/host/CuriosWTSubMenuHost2.java @@ -0,0 +1,21 @@ +package com.extendedae_plus.menu.host; + +import appeng.items.tools.powered.WirelessTerminalItem; +import appeng.helpers.WirelessTerminalMenuHost; +import appeng.api.storage.ISubMenuHost; +import appeng.menu.ISubMenu; +import appeng.menu.locator.ItemMenuHostLocator; +import net.minecraft.world.entity.player.Player; + +/** + * 使用 AE2 原生 WirelessTerminalItem 作为泛型参数,避免在运行时因为 wtlib API 类加载差异而无法强转为 ItemWT。 + * 实现 ISubMenuHost 以满足 CraftAmountMenu.open 所需宿主接口。 + */ +public class CuriosWTSubMenuHost2 extends WirelessTerminalMenuHost implements ISubMenuHost { + public CuriosWTSubMenuHost2(WirelessTerminalItem item, + Player player, + ItemMenuHostLocator locator, + java.util.function.BiConsumer returnToMainMenu) { + super(item, player, locator, returnToMainMenu); + } +} 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 4a9f296..ce002cd 100644 --- a/src/main/java/com/extendedae_plus/menu/locator/CuriosItemLocator.java +++ b/src/main/java/com/extendedae_plus/menu/locator/CuriosItemLocator.java @@ -26,6 +26,55 @@ public record CuriosItemLocator(String slotId, int index) implements ItemMenuHos @Nullable public T locate(Player player, Class hostInterface) { try { + // 先用 locateItem 取得实际物品,避免某些情况下 stacksHandler 为空 + ItemStack it0 = locateItem(player); + if (it0 != null && !it0.isEmpty()) { + // 0) AE2 原生:若是 WirelessTerminalItem,先尝试其原生 Host(与背包一致) + if (it0.getItem() instanceof appeng.items.tools.powered.WirelessTerminalItem wtAe2) { + var aeHost = wtAe2.getMenuHost(player, this, null); + if (aeHost != null && hostInterface.isInstance(aeHost)) { + try { return hostInterface.cast(aeHost); } catch (Throwable t) { } + } + if ("appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost2 = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost2(wtAe2, player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost2); } catch (Throwable t) { } + } + } + + WTDefinition def0 = WTDefinition.ofOrNull(it0); + if (def0 != null) { + WTMenuHost wtHost = def0.wTMenuHostFactory().create(def0.item(), player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + if (wtHost != null && hostInterface.isInstance(wtHost)) { + try { return hostInterface.cast(wtHost); } catch (Throwable t) { } + } + if ("appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost(def0.item(), player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost); } catch (Throwable t) { } + } + } else if (it0.getItem() instanceof de.mari_023.ae2wtlib.api.terminal.ItemWT wtItem0) { + if ("appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost(wtItem0, player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost); } catch (Throwable t) { } + } + } else { + // 额外兜底:尝试使用 WTDefinition.of(it0)(可能在 ofOrNull 为 null 时仍可获取) + try { + WTDefinition defStrict = WTDefinition.of(it0); + if (defStrict != null && "appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost(defStrict.item(), player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost); } catch (Throwable t) { } + } + } catch (Throwable t) { + + } + } + } + var opt = CuriosApi.getCuriosInventory(player); if (opt.isPresent()) { var handler = opt.get(); @@ -33,36 +82,72 @@ public record CuriosItemLocator(String slotId, int index) implements ItemMenuHos if (stacksHandler != null) { ItemStack it = stacksHandler.getStacks().getStackInSlot(index); if (!it.isEmpty()) { - // 1) ae2wtlib: 优先通过 WTDefinition 工厂创建 WTMenuHost(支持量子桥逻辑) - WTDefinition def = WTDefinition.ofOrNull(it); - if (def != null) { - WTMenuHost wtHost = def.wTMenuHostFactory().create( - def.item(), - player, - this, - (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this) - ); - if (hostInterface.isInstance(wtHost)) { - return hostInterface.cast(wtHost); + // 1) wtlib 优先:WTDefinition 构造 WTMenuHost + // 0) AE2 原生:若是 WirelessTerminalItem,先尝试其原生 Host(与背包一致) + if (it.getItem() instanceof appeng.items.tools.powered.WirelessTerminalItem wtAe2) { + var aeHost = wtAe2.getMenuHost(player, this, null); + if (aeHost != null && hostInterface.isInstance(aeHost)) { + try { return hostInterface.cast(aeHost); } catch (Throwable t) { } + } + if ("appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost2 = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost2(wtAe2, player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost2); } catch (Throwable t) { } } } - // 2) 回退:AE2 原生无线终端(IMenuItem) + WTDefinition def = WTDefinition.ofOrNull(it); + if (def != null) { + WTMenuHost wtHost = def.wTMenuHostFactory().create( + def.item(), player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + if (wtHost != null && hostInterface.isInstance(wtHost)) { + try { return hostInterface.cast(wtHost); } catch (Throwable t) { } + } + // 桥接 ISubMenuHost + if ("appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost(def.item(), player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost); } catch (Throwable t) { } + } + } else { + // 2) def==null,但物品是 wtlib 的 ItemWT:直接桥接 ISubMenuHost + if (it.getItem() instanceof de.mari_023.ae2wtlib.api.terminal.ItemWT wtItem) { + if ("appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost(wtItem, player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost); } catch (Throwable t) { } + } + } else { + // 再次兜底:尝试 WTDefinition.of(it) 强制获取 + try { + WTDefinition defStrict2 = WTDefinition.of(it); + if (defStrict2 != null && "appeng.api.storage.ISubMenuHost".equals(hostInterface.getName())) { + var subHost = new com.extendedae_plus.menu.host.CuriosWTSubMenuHost(defStrict2.item(), player, this, + (p, sub) -> MenuOpener.open(MEStorageMenu.WIRELESS_TYPE, p, this)); + try { return hostInterface.cast(subHost); } catch (Throwable t) { } + } + } catch (Throwable t) { + + } + } + } + + // 3) 回退:AE2 原生无线终端(IMenuItem) if (it.getItem() instanceof WirelessTerminalItem) { - // 由 IMenuItem 提供菜单宿主,定位器传入当前 CuriosItemLocator if (it.getItem() instanceof IMenuItem guiItem) { ItemMenuHost menuHost = guiItem.getMenuHost(player, this, null); - if (hostInterface.isInstance(menuHost)) { - return hostInterface.cast(menuHost); + if (menuHost != null && hostInterface.isInstance(menuHost)) { + try { return hostInterface.cast(menuHost); } catch (Throwable t) { } } } } else if (it.getItem() instanceof IMenuItem guiItem) { - // 回退:非无线终端,按常规 IMenuItem 处理 ItemMenuHost menuHost = guiItem.getMenuHost(player, this, null); - if (hostInterface.isInstance(menuHost)) { - return hostInterface.cast(menuHost); + if (menuHost != null && hostInterface.isInstance(menuHost)) { + try { return hostInterface.cast(menuHost); } catch (Throwable t) { } } } + } } } @@ -79,7 +164,8 @@ public record CuriosItemLocator(String slotId, int index) implements ItemMenuHos ICuriosItemHandler handler = opt.get(); ICurioStacksHandler stacksHandler = handler.getCurios().get(slotId); if (stacksHandler != null) { - return stacksHandler.getStacks().getStackInSlot(index); + ItemStack s = stacksHandler.getStacks().getStackInSlot(index); + return s; } } } catch (Throwable ignored) { diff --git a/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java b/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java index 13999c1..20b361e 100644 --- a/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java @@ -50,13 +50,46 @@ public class OpenCraftFromJeiC2SPacket implements CustomPacketPayload { var located = WirelessTerminalLocator.find(player); if (located.isEmpty()) return; - // 若为 Curios 槽位:直接用 CuriosItemLocator 作为宿主打开数量界面 + // 若为 Curios 槽位:根据终端类型分别处理 String curiosSlotId = located.getCuriosSlotId(); int curiosIndex = located.getCuriosIndex(); if (curiosSlotId != null && curiosIndex >= 0) { - int initial = 1; - CraftAmountMenu.open(player, new CuriosItemLocator(curiosSlotId, curiosIndex), what, initial); - return; + // 兼容 ae2wtlib 与 AE2 原生无线终端 + var stack = located.stack; + // AE2 原生无线终端路径需要做与背包一致的前置校验,避免打开后立即失败 + if (stack.getItem() instanceof WirelessTerminalItem wt) { + var grid = wt.getLinkedGrid(stack, player.level(), null); + if (grid == null) { return; } + if (!wt.hasPower(player, 0.5, stack)) { return; } + var craftingService = grid.getCraftingService(); + if (!craftingService.isCraftable(what)) { return; } + int initial = 1; + CraftAmountMenu.open(player, new CuriosItemLocator(curiosSlotId, curiosIndex), what, initial); + return; + } + + // wtlib 路径:先通过 WTMenuHost 获取 Grid 并检查是否可合成,再打开数量界面 + try { + de.mari_023.ae2wtlib.api.registration.WTDefinition def = de.mari_023.ae2wtlib.api.registration.WTDefinition.ofOrNull(stack); + if (def == null) { return; } + var wtHost = def.wTMenuHostFactory().create( + def.item(), + player, + new com.extendedae_plus.menu.locator.CuriosItemLocator(curiosSlotId, curiosIndex), + (p, sub) -> {} + ); + if (wtHost == null) { return; } + if (wtHost.getActionableNode() == null) { return; } + if (wtHost.getActionableNode().getGrid() == null) { return; } + var grid2 = wtHost.getActionableNode().getGrid(); + boolean craftable = grid2.getCraftingService().isCraftable(what); + if (!craftable) return; + int initial = 1; + CraftAmountMenu.open(player, new CuriosItemLocator(curiosSlotId, curiosIndex), what, initial); + return; + } catch (Throwable ignored) { + return; + } } // 非 Curios(主手/副手/背包)仍按原先流程做前置校验,保持行为一致。 diff --git a/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java b/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java index 6087fd4..1dd3517 100644 --- a/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/PickFromWirelessC2SPacket.java @@ -80,22 +80,26 @@ public class PickFromWirelessC2SPacket implements CustomPacketPayload { String curiosSlotId = located.getCuriosSlotId(); int curiosIndex = located.getCuriosIndex(); if (curiosSlotId != null && curiosIndex >= 0) { - WTDefinition def = WTDefinition.ofOrNull(terminal); - if (def != null) { + // 与 PullFromJeiOrCraftC2SPacket 保持一致:优先走 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) { + grid = wt.getLinkedGrid(terminal, level, null); + if (grid == null) return; + if (!wt.hasPower(player, 0.5, terminal)) return; + } else { + // 非 AE2 原生无线终端(极少数情况),再尝试 wtlib host + WTDefinition def = WTDefinition.ofOrNull(terminal); + if (def == null) return; WTMenuHost wtHost = def.wTMenuHostFactory().create( def.item(), player, new CuriosItemLocator(curiosSlotId, curiosIndex), (p, sub) -> {} ); - if (wtHost != null && wtHost.getActionableNode() != null && wtHost.getActionableNode().getGrid() != null) { - grid = wtHost.getActionableNode().getGrid(); - usedWtHost = true; - } else { - return; - } - } else { - return; + if (wtHost == null || wtHost.getActionableNode() == null || wtHost.getActionableNode().getGrid() == null) return; + grid = wtHost.getActionableNode().getGrid(); + usedWtHost = true; } } else { // AE2 原生路径 diff --git a/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java b/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java index 23116e5..6f820a0 100644 --- a/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/PullFromJeiOrCraftC2SPacket.java @@ -12,6 +12,7 @@ import appeng.items.tools.powered.WirelessTerminalItem; import appeng.me.helpers.PlayerSource; 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 com.extendedae_plus.util.WirelessTerminalLocator.LocatedTerminal; import net.minecraft.network.RegistryFriendlyByteBuf; @@ -89,6 +90,14 @@ public class PullFromJeiOrCraftC2SPacket implements CustomPacketPayload { var craftingService = grid.getCraftingService(); if (!craftingService.isCraftable(what)) return; + // Curios 槽位优先:使用 CuriosItemLocator 打开数量界面 + String curiosSlotId = located.getCuriosSlotId(); + int curiosIndex = located.getCuriosIndex(); + if (curiosSlotId != null && curiosIndex >= 0) { + CraftAmountMenu.open(player, new CuriosItemLocator(curiosSlotId, curiosIndex), what, 1); + return; + } + var hand = located.getHand(); int slot = located.getSlotIndex(); if (hand != null) {