样板供应器增加升级槽

This commit is contained in:
GaLicn 2025-09-25 18:01:49 +08:00
parent 687432f86f
commit 6d7e82e793
3 changed files with 294 additions and 6 deletions

View File

@ -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 = "<init>(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<ItemStack> 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);
}
}
}

View File

@ -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 {
// 安装 appfluxAE2/AppliedFlux 已在其原始构造流程中添加升级槽这里避免重复注入导致界面重复渲染
ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 跳过注入升级槽: 由 AE2/AppliedFlux 负责渲染");
}
}

View File

@ -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",