修复curios中无线终端相关操作异常

This commit is contained in:
GaLicn 2025-09-20 19:37:06 +08:00
parent 02c936ea0f
commit 8e74d13d73
7 changed files with 219 additions and 33 deletions

View File

@ -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<CraftingBlockEntity> b4 = (AEBaseEntityBlock<CraftingBlockEntity>) ModBlocks.ACCELERATOR_4x.get();
AEBaseEntityBlock<CraftingBlockEntity> b16 = (AEBaseEntityBlock<CraftingBlockEntity>) ModBlocks.ACCELERATOR_16x.get();
AEBaseEntityBlock<CraftingBlockEntity> b64 = (AEBaseEntityBlock<CraftingBlockEntity>) ModBlocks.ACCELERATOR_64x.get();

View File

@ -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<Player, ISubMenu> returnToMainMenu) {
super(item, player, locator, returnToMainMenu);
}
}

View File

@ -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<WirelessTerminalItem> implements ISubMenuHost {
public CuriosWTSubMenuHost2(WirelessTerminalItem item,
Player player,
ItemMenuHostLocator locator,
java.util.function.BiConsumer<Player, ISubMenu> returnToMainMenu) {
super(item, player, locator, returnToMainMenu);
}
}

View File

@ -26,6 +26,55 @@ public record CuriosItemLocator(String slotId, int index) implements ItemMenuHos
@Nullable
public <T> T locate(Player player, Class<T> 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) {

View File

@ -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主手/副手/背包仍按原先流程做前置校验保持行为一致

View File

@ -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 原生路径

View File

@ -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) {