From d60fe957d92fc469efbb91e5e7bcd19912ad915e Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 31 Oct 2025 00:01:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=87=AA=E5=8A=A8=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E7=A9=BA=E7=99=BD=E6=A0=B7=E6=9D=BF=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=B8=8E=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/PatternEncodingTermMenuMixin.java | 204 ++++++++---------- 1 file changed, 93 insertions(+), 111 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java index 6c76e0b..b166f10 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java @@ -8,7 +8,6 @@ import appeng.api.storage.MEStorage; import appeng.api.storage.StorageHelper; import appeng.core.definitions.AEItems; import appeng.helpers.IPatternTerminalMenuHost; -import appeng.menu.me.common.MEStorageMenu; import appeng.menu.me.items.PatternEncodingTermMenu; import appeng.menu.slot.RestrictedInputSlot; import com.extendedae_plus.mixin.ae2.accessor.MEStorageMenuAccessor; @@ -22,136 +21,119 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +/** + * 对 AE2 的 PatternEncodingTermMenu 添加自动补充空白样板功能。 + * 当玩家打开样板终端时,如果槽位为空且网络中存在空白样板,则会自动从网络提取若干放入槽中。 + * 若初始化时网络尚未激活,则会在第一次同步(broadcastChanges)时重试一次。 + */ @Mixin(PatternEncodingTermMenu.class) public abstract class PatternEncodingTermMenuMixin { - // 防止重复执行 - @Unique - private boolean eap$blankAutoFilled = false; - + /** 空白样板槽位(AE2原字段) */ @Final @Shadow(remap = false) private RestrictedInputSlot blankPatternSlot; + /** 防止重复自动填充的标记位 */ @Unique - private void eap$tryFill(IPatternTerminalMenuHost host, Inventory ip) { - try { - var self = (PatternEncodingTermMenu) (Object) this; - var player = ip.player; - // 仅在服务器端执行 - if (ip.player.level().isClientSide()) { - return; - } - // 必须可与网络交互 - var acc = (MEStorageMenuAccessor) (Object) ((MEStorageMenu) self); - MEStorage storage = acc.getStorage(); - IEnergySource power = acc.getPowerSource(); - boolean hasPower = acc.getHasPower(); - boolean canInteract = storage != null && power != null && hasPower; // 等价于 canInteractWithGrid() - if (!canInteract) { - return; - } - if (storage == null || power == null) { - return; - } - - InternalInventory blankInv = host.getLogic().getBlankPatternInv(); - var current = blankInv.getStackInSlot(0); - int limit = blankInv.getSlotLimit(0); - int space = Math.max(0, limit - current.getCount()); - space = Math.min(space, AEItems.BLANK_PATTERN.asItem().getMaxStackSize()); - if (space <= 0) { - return; // 已满,无需填充 - } - - AEKey blankKey = AEItemKey.of(AEItems.BLANK_PATTERN.asItem()); - long extracted = StorageHelper.poweredExtraction(power, storage, blankKey, space, - self.getActionSource()); - if (extracted <= 0) { - return; // 网络无可用空白样板 - } - - int toInsert = (int) Math.min(extracted, space); - var stackInSlot = this.blankPatternSlot.getItem(); - if (stackInSlot.isEmpty()) { - this.blankPatternSlot.set(AEItems.BLANK_PATTERN.stack(toInsert)); - } else { - stackInSlot.grow(toInsert); - this.blankPatternSlot.set(stackInSlot); - } - long leftover = extracted - toInsert; - if (leftover > 0) { - StorageHelper.poweredInsert(power, storage, blankKey, leftover, self.getActionSource()); - } - } catch (Throwable t) { - // swallow errors to avoid noisy logs in production - } - } + private boolean eap$blankAutoFilled = false; @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/IPatternTerminalMenuHost;Z)V", at = @At("TAIL"), remap = false) - private void eap$autoFillBlankPattern(MenuType menuType, int id, Inventory ip, - IPatternTerminalMenuHost host, boolean bindInventory, - CallbackInfo ci) { - eap$tryFill(host, ip); + private void eap$onCtor1(MenuType menuType, int id, Inventory ip, IPatternTerminalMenuHost host, + boolean bindInventory, CallbackInfo ci) { + eap$tryAutoFill(ip, host, false); } @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/IPatternTerminalMenuHost;)V", at = @At("TAIL"), remap = false) - private void eap$autoFillCtor3(int id, Inventory ip, IPatternTerminalMenuHost host, CallbackInfo ci) { - eap$tryFill(host, ip); + private void eap$onCtor2(int id, Inventory ip, IPatternTerminalMenuHost host, CallbackInfo ci) { + eap$tryAutoFill(ip, host, false); } - // 在首次 broadcastChanges 后再尝试一次,避免构造时网络未激活 + /** + * 当容器同步(broadcastChanges)时再尝试一次自动填充。 + * 某些情况下,构造时网络尚未激活(storage/power 为空), + * 因此需要延迟到第一次同步后执行补偿填充。 + */ @Inject(method = "broadcastChanges", at = @At("TAIL")) - private void eap$retryFillAfterPower(CallbackInfo ci) { - if (this.eap$blankAutoFilled) { - return; - } - // 仅在服务器端执行 - var self = (PatternEncodingTermMenu) (Object) this; - var player = self.getPlayerInventory().player; - var acc = (MEStorageMenuAccessor) (Object) ((MEStorageMenu) self); - MEStorage storage = acc.getStorage(); - IEnergySource power = acc.getPowerSource(); - boolean hasPower = acc.getHasPower(); - if (player.level().isClientSide()) { - return; - } - boolean canInteract = storage != null && power != null && hasPower; - if (!canInteract) { - return; - } - // 通过 host 获取 blankPatternInv - var host = ((IPatternTerminalMenuHost) self.getTarget()); - InternalInventory blankInv = host.getLogic().getBlankPatternInv(); - var current = blankInv.getStackInSlot(0); - int limit = blankInv.getSlotLimit(0); - int space = Math.max(0, limit - current.getCount()); - space = Math.min(space, AEItems.BLANK_PATTERN.asItem().getMaxStackSize()); - if (space <= 0) { - this.eap$blankAutoFilled = true; - return; - } + private void eap$onBroadcastChanges(CallbackInfo ci) { + if (!eap$blankAutoFilled) { + var self = (PatternEncodingTermMenu) (Object) this; + var player = self.getPlayerInventory().player; - AEKey blankKey = AEItemKey.of(AEItems.BLANK_PATTERN.asItem()); - long extracted = StorageHelper.poweredExtraction(power, storage, blankKey, space, - self.getActionSource()); - if (extracted <= 0) { - return; + // 仅在服务端执行逻辑 + if (!player.level().isClientSide()) { + eap$tryAutoFill(self.getPlayerInventory(), (IPatternTerminalMenuHost) self.getTarget(), true); + } } - int toInsert = (int) Math.min(extracted, space); - var stackInSlot = this.blankPatternSlot.getItem(); - if (stackInSlot.isEmpty()) { - this.blankPatternSlot.set(AEItems.BLANK_PATTERN.stack(toInsert)); - } else { - stackInSlot.grow(toInsert); - this.blankPatternSlot.set(stackInSlot); + } + + /** + * 尝试自动从网络提取空白样板并填入终端槽位。 + * + * @param ip 玩家背包对象(用于获取 Player 实例) + * @param host 菜单宿主(提供 blankPatternInv 等逻辑访问) + * @param markFilled 是否在执行后标记为“已填充过”(用于 broadcastChanges 重试逻辑) + */ + @Unique + private void eap$tryAutoFill(Inventory ip, IPatternTerminalMenuHost host, boolean markFilled) { + try { + var self = (PatternEncodingTermMenu) (Object) this; + var player = ip.player; + + // 跳过客户端执行(避免无效提取) + if (player.level().isClientSide()) return; + + // 获取 AE2 存储访问接口 + var accessor = (MEStorageMenuAccessor) self; + MEStorage storage = accessor.getStorage(); + IEnergySource power = accessor.getPowerSource(); + + // 无法交互(例如网络未激活或无电力)则跳过 + if (storage == null || power == null || !accessor.getHasPower()) return; + + // 获取空白样板的专用内部槽 + InternalInventory blankInv = host.getLogic().getBlankPatternInv(); + var current = blankInv.getStackInSlot(0); + int limit = blankInv.getSlotLimit(0); + + // 计算当前剩余可放入数量(不超过物品最大堆叠) + int space = Math.min(Math.max(0, limit - current.getCount()), + AEItems.BLANK_PATTERN.asItem().getMaxStackSize()); + + // 槽位已满时直接跳过 + if (space <= 0) { + if (markFilled) eap$blankAutoFilled = true; + return; + } + + // 从网络中提取空白样板 + AEKey blankKey = AEItemKey.of(AEItems.BLANK_PATTERN.asItem()); + long extracted = StorageHelper.poweredExtraction(power, storage, blankKey, space, self.getActionSource()); + if (extracted <= 0) return; + + // 插入到空白样板槽中 + int toInsert = (int) Math.min(extracted, space); + var slotStack = blankPatternSlot.getItem(); + if (slotStack.isEmpty()) { + blankPatternSlot.set(AEItems.BLANK_PATTERN.stack(toInsert)); + } else { + slotStack.grow(toInsert); + blankPatternSlot.set(slotStack); + } + + // 若存在提取但未放入完的剩余物品,退还到网络中 + long leftover = extracted - toInsert; + if (leftover > 0) { + StorageHelper.poweredInsert(power, storage, blankKey, leftover, self.getActionSource()); + } + + // 成功或确定无剩余后标记为已填充,防止重复执行 + if (markFilled) eap$blankAutoFilled = true; + + } catch (Exception ignored) { + // 安全兜底,防止 AE2 网络状态异常或空指针导致的崩溃 } - long leftover = extracted - toInsert; - if (leftover > 0) { - StorageHelper.poweredInsert(power, storage, blankKey, leftover, self.getActionSource()); - } - this.eap$blankAutoFilled = true; } }