From 6d7e82e7938f349a8f7b2ed115b64d1690f757f6 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:01:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94=E5=99=A8?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8D=87=E7=BA=A7=E6=A7=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PatternProviderLogicUpgradesMixin.java | 279 ++++++++++++++++++ .../PatternProviderMenuUpgradesMixin.java | 20 +- .../resources/extendedae_plus.mixins.json | 1 + 3 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java new file mode 100644 index 0000000..8b2f41c --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java @@ -0,0 +1,279 @@ +package com.extendedae_plus.mixin.ae2.helpers; + +import appeng.api.networking.IManagedGridNode; +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.api.upgrades.UpgradeInventories; +import appeng.helpers.patternprovider.PatternProviderLogic; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import com.extendedae_plus.util.ExtendedAELogger; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.fml.ModList; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.util.List; + +/** + * 为样板供应器添加升级槽支持 + * - 没有 AppliedFlux 时:添加 1 个升级槽 + * - 有 AppliedFlux 时:在其基础上再增加 1 个(总共 2 个) + * + * 优先级 1100 确保在 AppliedFlux (默认优先级 1000) 之后执行 + */ +@Mixin(value = PatternProviderLogic.class, priority = 1100, remap = false) +public abstract class PatternProviderLogicUpgradesMixin { + + @Final + @Shadow + private PatternProviderLogicHost host; + + @Final + @Shadow + private IManagedGridNode mainNode; + + @Unique + private IUpgradeInventory eap$upgrades = UpgradeInventories.empty(); + + @Unique + private boolean eap$hasAppliedFlux = false; + + @Unique + private boolean eap$upgradesInitialized = false; + + @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V", + at = @At("TAIL")) + private void eap$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { + try { + // 检测是否安装了 AppliedFlux + this.eap$hasAppliedFlux = ModList.get().isLoaded("appflux"); + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] AppliedFlux 状态: {}", eap$hasAppliedFlux); + + if (eap$hasAppliedFlux) { + // AppliedFlux 已安装,尝试获取并扩展其升级槽 + eap$extendAppliedFluxUpgrades(); + } // 未安装 AppliedFlux 的情况由 CompatMixin 负责创建兼容升级槽 + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 初始化失败", t); + } + } + + @Unique + private void eap$extendAppliedFluxUpgrades() { + try { + // 通过反射直接读取 AppliedFlux Mixin 注入的字段:af_upgrades + IUpgradeInventory existingUpgrades = null; + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + existingUpgrades = (IUpgradeInventory) f.get(this); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 读取 AF 升级槽字段失败: {}", t.getMessage()); + } + + if (existingUpgrades != null && existingUpgrades != UpgradeInventories.empty()) { + // AppliedFlux 已经创建了升级槽 + int currentSlots = existingUpgrades.size(); + int targetSlots = 2; // AppliedFlux 1个 + 我们 1个 = 2个 + + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] AppliedFlux 升级槽数量: {}, 目标: {}", + currentSlots, targetSlots); + + if (currentSlots < targetSlots) { + // 需要扩展升级槽 + // 先保存现有物品 + ItemStack[] savedItems = new ItemStack[currentSlots]; + for (int i = 0; i < currentSlots; i++) { + savedItems[i] = existingUpgrades.getStackInSlot(i).copy(); + } + + // 创建新的升级槽(更多槽位) + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + targetSlots, + this::eap$onUpgradesChanged + ); + + // 恢复原有物品 + for (int i = 0; i < savedItems.length; i++) { + this.eap$upgrades.setItemDirect(i, savedItems[i]); + } + // 将 AF 的字段指向我们新的升级槽,保持其服务与 NBT 钩子一致 + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + f.set(this, this.eap$upgrades); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 绑定 AF 升级槽字段失败: {}", t.getMessage()); + } + + this.eap$upgradesInitialized = true; + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 扩展到 {} 个升级槽", targetSlots); + } else { + // AppliedFlux 或其他模组已经提供了足够的槽位 + this.eap$upgrades = existingUpgrades; + this.eap$upgradesInitialized = true; + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 使用现有 {} 个升级槽", currentSlots); + } + } else { + // AppliedFlux 还没初始化升级槽,或者出了问题,我们创建默认的 + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 2, + this::eap$onUpgradesChanged + ); + // 同步 AF 字段 + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + f.set(this, this.eap$upgrades); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 初始化时绑定 AF 升级槽字段失败: {}", t.getMessage()); + } + this.eap$upgradesInitialized = true; + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 创建 2 个升级槽(AppliedFlux 未初始化)"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 扩展 AppliedFlux 升级槽失败", t); + // 失败时创建默认数量 + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 2, + this::eap$onUpgradesChanged + ); + // 同步 AF 字段(最佳努力) + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + f.set(this, this.eap$upgrades); + } catch (Throwable ignored) {} + this.eap$upgradesInitialized = true; + } + } + + @Unique + private void eap$onUpgradesChanged() { + try { + this.host.saveChanges(); + + // 如果 AppliedFlux 安装了,也调用其原始的 onUpgradesChanged 方法 + if (eap$hasAppliedFlux) { + try { + Method afMethod = this.getClass().getDeclaredMethod("af_onUpgradesChanged"); + afMethod.setAccessible(true); + afMethod.invoke(this); + } catch (NoSuchMethodException e) { + // AppliedFlux 的方法不存在,这是正常的 + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 调用 AppliedFlux onUpgradesChanged 失败: {}", t.getMessage()); + } + } + + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 升级槽内容已变更"); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] onUpgradesChanged 处理失败", t); + } + } + + + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void eap$saveUpgrades(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + try { + // 已安装 AF 时交由 AF 的 mixin 处理,避免重复写入 + if (eap$hasAppliedFlux) { + return; + } + if (eap$upgradesInitialized && this.eap$upgrades != null && this.eap$upgrades != UpgradeInventories.empty()) { + // 根据是否有 AppliedFlux 使用不同的 NBT 键 + if (eap$hasAppliedFlux) { + // AppliedFlux 使用 "upgrades" 键,我们使用 "eap_upgrades" 避免冲突 + this.eap$upgrades.writeToNBT(tag, "eap_upgrades", registries); + } else { + // 没有 AppliedFlux,使用标准键 + this.eap$upgrades.writeToNBT(tag, "upgrades", registries); + } + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 保存升级槽到 NBT"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 保存升级槽失败", t); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$loadUpgrades(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + try { + // 重新检测 AppliedFlux 状态(可能在世界加载时状态有变化) + this.eap$hasAppliedFlux = ModList.get().isLoaded("appflux"); + + if (!eap$upgradesInitialized) { + // 如果还没初始化,先初始化 + if (eap$hasAppliedFlux) { + eap$extendAppliedFluxUpgrades(); + } else { + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 1, + this::eap$onUpgradesChanged + ); + this.eap$upgradesInitialized = true; + } + } + + // 已安装 AF 时,由 AF 自行从 "upgrades" 读取;我们只处理无 AF 情况 + if (!eap$hasAppliedFlux && this.eap$upgrades != null && this.eap$upgrades != UpgradeInventories.empty()) { + if (tag.contains("upgrades")) { + this.eap$upgrades.readFromNBT(tag, "upgrades", registries); + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 从 upgrades 加载升级槽"); + } + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 加载升级槽失败", t); + } + } + + @Inject(method = "addDrops", at = @At("TAIL")) + private void eap$dropUpgrades(List drops, CallbackInfo ci) { + try { + // AF 已安装时交由其自身处理掉落 + if (eap$hasAppliedFlux) { + return; + } + if (eap$upgradesInitialized && this.eap$upgrades != null) { + for (var is : this.eap$upgrades) { + if (!is.isEmpty()) { + drops.add(is); + } + } + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 添加升级槽物品到掉落列表"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 添加掉落失败", t); + } + } + + @Inject(method = "clearContent", at = @At("TAIL")) + private void eap$clearUpgrades(CallbackInfo ci) { + try { + // AF 已安装时交由其自身处理清理 + if (eap$hasAppliedFlux) { + return; + } + if (eap$upgradesInitialized && this.eap$upgrades != null) { + this.eap$upgrades.clear(); + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 清空升级槽"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 清空升级槽失败", t); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java index 17f2f60..385bd31 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java @@ -8,6 +8,7 @@ import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.bridge.IUpgradableMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; import com.extendedae_plus.bridge.CompatUpgradeProvider; +import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; @@ -32,13 +33,20 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem at = @At("TAIL")) private void eap$initUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { this.eap$toolbox = new ToolboxMenu(this); - if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - // 未安装 appflux:使用我们提供的升级槽 - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 注入升级槽: 使用自带 compat 槽"); + + // 现在 PatternProviderLogic 始终实现 IUpgradeableObject(通过我们的 mixin) + if (this.logic instanceof IUpgradeableObject upgradeableLogic) { + IUpgradeInventory upgrades = upgradeableLogic.getUpgrades(); + if (upgrades != null && upgrades != appeng.api.upgrades.UpgradeInventories.empty()) { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 设置升级槽 UI,槽位数: {}", upgrades.size()); + this.setupUpgrades(upgrades); + } else { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 升级槽为空或未初始化"); + } + } else if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 备用方案:使用 compat 升级槽 + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 备用方案:使用 compat 升级槽"); this.setupUpgrades(((CompatUpgradeProvider) this.logic).eap$getCompatUpgrades()); - } else { - // 安装 appflux:AE2/AppliedFlux 已在其原始构造流程中添加升级槽,这里避免重复注入导致界面重复渲染 - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 跳过注入升级槽: 由 AE2/AppliedFlux 负责渲染"); } } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 102ab73..1aec0a9 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -31,6 +31,7 @@ "ae2.helpers.InterfaceLogicUpgradesMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", + "ae2.helpers.PatternProviderLogicUpgradesMixin", "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2.menu.InterfaceMenuUpgradesMixin", "ae2.menu.PatternProviderMenuUpgradesMixin",