diff --git a/build.gradle b/build.gradle index 25fe6a4..f22d7c4 100644 --- a/build.gradle +++ b/build.gradle @@ -81,7 +81,7 @@ dependencies { annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor" - modImplementation "curse.maven:applied-flux-965012:6755986" + // modImplementation "curse.maven:applied-flux-965012:6755986" modCompileOnly "curse.maven:mega-cells-622112:${mega_cells_version}" modCompileOnly "curse.maven:jade-324717:${jade_version}" diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 9d993f8..efc4a7a 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -4,6 +4,7 @@ import appeng.api.storage.StorageCells; import appeng.menu.locator.MenuLocators; import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler; import com.extendedae_plus.client.ClientRegistrar; +import com.extendedae_plus.compat.CompatibilityTest; import com.extendedae_plus.config.ModConfig; import com.extendedae_plus.init.*; import com.extendedae_plus.menu.locator.CuriosItemLocator; @@ -62,6 +63,9 @@ public class ExtendedAEPlus { private void commonSetup(final FMLCommonSetupEvent event) { StorageCells.addCellHandler(InfinityBigIntegerCellHandler.INSTANCE); + // 运行兼容性测试 + CompatibilityTest.testCompatibility(); + // 注册本模组网络通道与数据包 event.enqueueWork(() -> { // 注册升级卡 diff --git a/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java new file mode 100644 index 0000000..50b7687 --- /dev/null +++ b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java @@ -0,0 +1,62 @@ +package com.extendedae_plus.compat; + +import com.extendedae_plus.util.ExtendedAELogger; +import net.minecraftforge.fml.ModList; + +/** + * 兼容性测试类 + * 用于验证模组兼容性检测是否正常工作 + */ +public class CompatibilityTest { + + /** + * 测试模组兼容性检测 + */ + public static void testCompatibility() { + ExtendedAELogger.LOGGER.info("=== ExtendedAE_Plus 兼容性测试开始 ==="); + + // 测试appflux模组检测 + boolean appfluxExists = ModList.get().isLoaded("appflux"); + ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测结果: {}", appfluxExists ? "存在" : "不存在"); + + // 测试升级卡槽功能启用状态 + boolean shouldEnableUpgrades = UpgradeSlotCompat.shouldEnableUpgradeSlots(); + ExtendedAELogger.LOGGER.info("升级卡槽功能启用状态: {}", shouldEnableUpgrades ? "启用" : "禁用"); + + // 测试Screen升级面板添加状态 + boolean shouldAddPanel = UpgradeSlotCompat.shouldAddUpgradePanelToScreen(); + ExtendedAELogger.LOGGER.info("Screen升级面板添加状态: {}", shouldAddPanel ? "启用" : "禁用"); + + // 输出兼容性策略 + if (appfluxExists) { + ExtendedAELogger.LOGGER.info("兼容性策略: 检测到ExtendedAE-appflux模组,将使用其升级卡槽功能"); + } else { + ExtendedAELogger.LOGGER.info("兼容性策略: 未检测到ExtendedAE-appflux模组,将使用我们自己的升级卡槽功能"); + } + + ExtendedAELogger.LOGGER.info("=== ExtendedAE_Plus 兼容性测试完成 ==="); + } + + /** + * 获取兼容性状态报告 + */ + public static String getCompatibilityReport() { + boolean appfluxExists = ModList.get().isLoaded("appflux"); + boolean upgradesEnabled = UpgradeSlotCompat.shouldEnableUpgradeSlots(); + + StringBuilder report = new StringBuilder(); + report.append("ExtendedAE_Plus 兼容性报告:\n"); + report.append("- ExtendedAE-appflux模组: ").append(appfluxExists ? "已安装" : "未安装").append("\n"); + report.append("- 升级卡槽功能: ").append(upgradesEnabled ? "启用中" : "已禁用").append("\n"); + + if (appfluxExists && !upgradesEnabled) { + report.append("- 兼容性状态: 正常 (使用appflux的升级功能)\n"); + } else if (!appfluxExists && upgradesEnabled) { + report.append("- 兼容性状态: 正常 (使用我们的升级功能)\n"); + } else { + report.append("- 兼容性状态: 异常 (配置不一致)\n"); + } + + return report.toString(); + } +} diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java new file mode 100644 index 0000000..6c39295 --- /dev/null +++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java @@ -0,0 +1,202 @@ +package com.extendedae_plus.compat; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.api.upgrades.UpgradeInventories; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.ToolboxPanel; +import appeng.client.gui.widgets.UpgradesPanel; +import appeng.core.localization.GuiText; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import appeng.menu.AEBaseMenu; +import appeng.menu.SlotSemantics; +import appeng.menu.ToolboxMenu; +import com.extendedae_plus.util.ExtendedAELogger; +import net.minecraft.network.chat.Component; +import net.minecraft.world.inventory.Slot; +import net.minecraftforge.fml.ModList; + +import java.util.ArrayList; +import java.util.List; + +/** + * 升级卡槽兼容性管理类 + * 检测ExtendedAE-appflux模组是否存在,如果存在则使用其升级卡槽功能 + * 否则使用我们自己的实现 + */ +public class UpgradeSlotCompat { + private static final String APPFLUX_MOD_ID = "appflux"; + + /** + * 检测是否应该启用我们的升级卡槽功能 + * @return true如果应该启用,false如果检测到appflux模组存在 + */ + public static boolean shouldEnableUpgradeSlots() { + boolean appfluxExists = ModList.get().isLoaded(APPFLUX_MOD_ID); + ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测: {}", appfluxExists ? "存在" : "不存在"); + + if (appfluxExists) { + ExtendedAELogger.LOGGER.info("检测到ExtendedAE-appflux模组,跳过我们的升级卡槽功能"); + return false; + } else { + ExtendedAELogger.LOGGER.info("未检测到ExtendedAE-appflux模组,启用我们的升级卡槽功能"); + return true; + } + } + + /** + * 检测是否应该在Screen中添加升级面板 + * @return true如果应该添加,false如果检测到appflux模组存在 + */ + public static boolean shouldAddUpgradePanelToScreen() { + return shouldEnableUpgradeSlots(); + } + + /** + * 初始化菜单升级功能(如果需要的话) + * @param menu 目标菜单 + * @param host 样板供应器逻辑主机 + * @return 是否成功初始化 + */ + public static boolean initMenuUpgrades(AEBaseMenu menu, PatternProviderLogicHost host) { + if (!shouldEnableUpgradeSlots()) { + return false; + } + + try { + // 创建工具箱菜单 + ToolboxMenu toolbox = new ToolboxMenu(menu); + + // 设置升级槽 + if (host instanceof IUpgradeableObject upgradeableHost) { + // 使用反射调用protected的setupUpgrades方法 + try { + var setupUpgradesMethod = AEBaseMenu.class.getDeclaredMethod("setupUpgrades", IUpgradeInventory.class); + setupUpgradesMethod.setAccessible(true); + setupUpgradesMethod.invoke(menu, upgradeableHost.getUpgrades()); + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("反射调用setupUpgrades失败", e); + return false; + } + + // 使用反射或接口设置工具箱 + if (menu instanceof IUpgradeableMenuCompat compatMenu) { + compatMenu.setCompatToolbox(toolbox); + } + + ExtendedAELogger.LOGGER.debug("成功为PatternProviderMenu初始化升级功能"); + return true; + } + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("初始化PatternProviderMenu升级功能时出错", e); + } + + return false; + } + + /** + * 为Screen添加升级面板(如果需要的话) + * @param widgets 小部件映射 + * @param menu 菜单实例 + * @param style 屏幕样式 + * @return 是否成功添加 + */ + public static boolean addUpgradePanelToScreen(Object widgets, Object menu, ScreenStyle style) { + if (!shouldAddUpgradePanelToScreen()) { + return false; + } + + try { + if (menu instanceof IUpgradeableMenuCompat compatMenu) { + try { + // 使用反射获取widgets的add方法 - 尝试不同的方法签名 + var widgetsClass = widgets.getClass(); + var addMethod = widgetsClass.getDeclaredMethod("add", String.class, Object.class); + addMethod.setAccessible(true); + + // 获取升级槽位 + var menuClass = menu.getClass(); + var getSlotsMethod = menuClass.getMethod("getSlots", SlotSemantics.class); + @SuppressWarnings("unchecked") + List upgradeSlots = (List) getSlotsMethod.invoke(menu, SlotSemantics.UPGRADE); + + // 添加升级面板 + UpgradesPanel upgradesPanel = new UpgradesPanel(upgradeSlots, () -> getCompatibleUpgrades(compatMenu)); + addMethod.invoke(widgets, "upgrades", upgradesPanel); + + // 添加工具箱面板(如果存在) + ToolboxMenu toolbox = compatMenu.getCompatToolbox(); + if (toolbox != null && toolbox.isPresent()) { + ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName()); + addMethod.invoke(widgets, "toolbox", toolboxPanel); + } + + ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板"); + return true; + } catch (NoSuchMethodException e) { + // 尝试其他可能的方法签名 + try { + var widgetsClass = widgets.getClass(); + var putMethod = widgetsClass.getDeclaredMethod("put", String.class, Object.class); + putMethod.setAccessible(true); + + // 获取升级槽位 + var menuClass = menu.getClass(); + var getSlotsMethod = menuClass.getMethod("getSlots", SlotSemantics.class); + @SuppressWarnings("unchecked") + List upgradeSlots = (List) getSlotsMethod.invoke(menu, SlotSemantics.UPGRADE); + + // 添加升级面板 + UpgradesPanel upgradesPanel = new UpgradesPanel(upgradeSlots, () -> getCompatibleUpgrades(compatMenu)); + putMethod.invoke(widgets, "upgrades", upgradesPanel); + + // 添加工具箱面板(如果存在) + ToolboxMenu toolbox = compatMenu.getCompatToolbox(); + if (toolbox != null && toolbox.isPresent()) { + ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName()); + putMethod.invoke(widgets, "toolbox", toolboxPanel); + } + + ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板(使用put方法)"); + return true; + } catch (Exception e2) { + ExtendedAELogger.LOGGER.error("反射调用widgets方法失败", e2); + return false; + } + } + } + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("为PatternProviderScreen添加升级面板时出错", e); + } + + return false; + } + + /** + * 获取兼容的升级列表 + */ + private static List getCompatibleUpgrades(IUpgradeableMenuCompat menu) { + var list = new ArrayList(); + list.add(GuiText.CompatibleUpgrades.text()); + + try { + IUpgradeInventory upgrades = menu.getCompatUpgrades(); + if (upgrades != null) { + list.addAll(appeng.api.upgrades.Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem())); + } + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("获取兼容升级列表时出错", e); + } + + return list; + } + + /** + * 兼容性升级菜单接口 + */ + public interface IUpgradeableMenuCompat { + ToolboxMenu getCompatToolbox(); + void setCompatToolbox(ToolboxMenu toolbox); + IUpgradeInventory getCompatUpgrades(); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java new file mode 100644 index 0000000..ed45cbe --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java @@ -0,0 +1,79 @@ +package com.extendedae_plus.mixin; + +import net.minecraftforge.fml.ModList; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +/** + * Mixin条件加载插件 + * 用于根据模组存在情况动态加载不同的Mixin + */ +public class MixinConditions implements IMixinConfigPlugin { + + @Override + public void onLoad(String mixinPackage) { + // 初始化时调用 + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + // 对于升级相关的Mixin,检查appflux是否存在 + if (mixinClassName.contains("PatternProviderMenuUpgradesMixin") || + mixinClassName.contains("PatternProviderScreenUpgradesMixin") || + mixinClassName.contains("PatternProviderLogicUpgradesMixin") || + mixinClassName.contains("PatternProviderLogicHostUpgradesMixin")) { + + try { + // 检查ModList是否已初始化 + if (net.minecraftforge.fml.ModList.get() == null) { + System.out.println("[ExtendedAE_Plus] ModList未初始化,默认应用升级Mixin: " + mixinClassName); + return true; // 修改策略:未初始化时默认应用,运行时再检查 + } + + boolean appfluxExists = net.minecraftforge.fml.ModList.get().isLoaded("appflux"); + boolean shouldApply = !appfluxExists; + + System.out.println("[ExtendedAE_Plus] 升级Mixin检查: " + mixinClassName + + ", appflux存在: " + appfluxExists + + ", 应用Mixin: " + shouldApply); + + return shouldApply; + } catch (Exception e) { + System.out.println("[ExtendedAE_Plus] ModList检查失败,默认应用升级Mixin: " + mixinClassName); + return true; // 修改策略:出错时默认应用,运行时再检查 + } + } + + // 其他Mixin正常应用 + System.out.println("[ExtendedAE_Plus] 加载Mixin: " + mixinClassName); + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + // 接受目标类 + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, org.objectweb.asm.tree.ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + // 应用前调用 + } + + @Override + public void postApply(String targetClassName, org.objectweb.asm.tree.ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + // 应用后调用 + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java index 9e84b7f..d02b4f9 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java @@ -10,6 +10,7 @@ import appeng.core.localization.GuiText; import appeng.menu.SlotSemantics; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.bridge.IUpgradableMenu; +import com.extendedae_plus.compat.UpgradeSlotCompat; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; import org.spongepowered.asm.mixin.Mixin; @@ -21,15 +22,20 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.ArrayList; import java.util.List; -@Mixin(value = PatternProviderScreen.class, remap = false) +@Mixin(value = PatternProviderScreen.class, priority = 2000, remap = false) public abstract class PatternProviderScreenUpgradesMixin extends AEBaseScreen { @Inject(method = "", at = @At("TAIL")) private void eap$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + // 只有在应该启用升级卡槽时才添加升级面板 + if (!UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) { + return; + } + this.widgets.add("upgrades", new UpgradesPanel( menu.getSlots(SlotSemantics.UPGRADE), this::eap$getCompatibleUpgrades)); - if (((IUpgradableMenu) menu).getToolbox().isPresent()) { + if (((IUpgradableMenu) menu).getToolbox() != null && ((IUpgradableMenu) menu).getToolbox().isPresent()) { this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName())); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java new file mode 100644 index 0000000..edba48a --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java @@ -0,0 +1,69 @@ +package com.extendedae_plus.mixin.ae2.compat; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import appeng.menu.AEBaseMenu; +import appeng.menu.ToolboxMenu; +import appeng.menu.implementations.PatternProviderMenu; +import com.extendedae_plus.compat.UpgradeSlotCompat; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import org.spongepowered.asm.mixin.Mixin; +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; + +/** + * PatternProviderMenu的兼容性Mixin + * 优先级设置为500,低于appflux的默认优先级,避免冲突 + */ +@Mixin(value = PatternProviderMenu.class, priority = 500, remap = false) +public abstract class PatternProviderCompatMixin extends AEBaseMenu implements UpgradeSlotCompat.IUpgradeableMenuCompat { + + @Unique + private ToolboxMenu eap$compatToolbox; + + @Unique + private IUpgradeInventory eap$compatUpgrades; + + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", + at = @At("TAIL")) + private void eap$initCompatUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { + try { + // 检测是否应该启用升级卡槽功能 + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 直接初始化升级功能 + this.eap$compatToolbox = new ToolboxMenu(this); + + if (host instanceof appeng.api.upgrades.IUpgradeableObject upgradeableHost) { + this.eap$compatUpgrades = upgradeableHost.getUpgrades(); + this.setupUpgrades(this.eap$compatUpgrades); + } + } + } catch (Exception e) { + // 静默处理异常,确保不会因为升级功能导致崩溃 + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e); + } + } + + @Override + public ToolboxMenu getCompatToolbox() { + return this.eap$compatToolbox; + } + + @Override + public void setCompatToolbox(ToolboxMenu toolbox) { + this.eap$compatToolbox = toolbox; + } + + @Override + public IUpgradeInventory getCompatUpgrades() { + return this.eap$compatUpgrades; + } + + // 构造函数,Mixin要求 + public PatternProviderCompatMixin(MenuType menuType, int id, Inventory playerInventory, Object host) { + super(menuType, id, playerInventory, host); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java new file mode 100644 index 0000000..0945b85 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java @@ -0,0 +1,337 @@ +package com.extendedae_plus.mixin.ae2.compat; + +import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.security.IActionSource; +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.ae.items.ChannelCardItem; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.wireless.WirelessSlaveLink; +import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +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.util.List; + +/** + * PatternProviderLogic的兼容性Mixin + * 优先级设置为500,避免与appflux冲突 + */ +@Mixin(value = PatternProviderLogic.class, priority = 500, remap = false) +public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge { + + @Unique + private IUpgradeInventory eap$compatUpgrades = UpgradeInventories.empty(); + + @Unique + private WirelessSlaveLink eap$compatLink; + + @Unique + private long eap$compatLastChannel = -1; + + @Unique + private boolean eap$compatClientConnected = false; + + @Unique + private boolean eap$compatHasInitialized = false; + + @Unique + private int eap$compatDelayedInitTicks = 0; + + @Final + @Shadow + private PatternProviderLogicHost host; + + @Final + @Shadow + private IManagedGridNode mainNode; + + @Final + @Shadow + private IActionSource actionSource; + + @Unique + private void eap$compatOnUpgradesChanged() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + + try { + this.host.saveChanges(); + // 升级变更,重置并尝试初始化 + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatInitializeChannelLink(); + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级变更处理失败", e); + } + } + + @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V", + at = @At("TAIL")) + private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 1, + this::eap$compatOnUpgradesChanged + ); + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级初始化失败", e); + } + } + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void eap$compatSaveUpgrades(CompoundTag tag, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades"); + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级保存失败", e); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$compatLoadUpgrades(CompoundTag tag, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades"); + // 从 NBT 加载后,重置并尝试初始化 + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatInitializeChannelLink(); + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级加载失败", e); + } + } + + @Inject(method = "addDrops", at = @At("TAIL")) + private void eap$compatDropUpgrades(List drops, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + for (var stack : this.eap$compatUpgrades) { + if (!stack.isEmpty()) { + drops.add(stack); + } + } + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级掉落失败", e); + } + } + + @Inject(method = "clearContent", at = @At("TAIL")) + private void eap$compatClearUpgrades(CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades.clear(); + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级清理失败", e); + } + } + + @Override + public IUpgradeInventory getUpgrades() { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return this.eap$compatUpgrades; + } + return UpgradeInventories.empty(); + } + + @Override + public void eap$updateWirelessLink() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + + try { + if (eap$compatLink != null) { + eap$compatLink.updateStatus(); + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性无线链接更新失败", e); + } + } + + @Unique + public void eap$compatInitializeChannelLink() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + + try { + // 客户端早退 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return; + } + + // 避免重复初始化 + if (eap$compatHasInitialized) { + return; + } + + // 等待网格完成引导 + if (!mainNode.hasGridBooted()) { + eap$compatDelayedInitTicks = Math.max(eap$compatDelayedInitTicks, 5); + try { + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + } catch (Throwable ignored) {} + return; + } + + long channel = 0L; + boolean found = false; + for (ItemStack stack : this.eap$compatUpgrades) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + found = true; + break; + } + } + + if (!found) { + // 无频道卡:断开并视为初始化完成 + if (eap$compatLink != null) { + eap$compatLink.setFrequency(0L); + eap$compatLink.updateStatus(); + } + eap$compatHasInitialized = true; + return; + } + + if (eap$compatLink == null) { + var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode()); + eap$compatLink = new WirelessSlaveLink(endpoint); + } + + eap$compatLink.setFrequency(channel); + eap$compatLink.updateStatus(); + + if (eap$compatLink.isConnected()) { + eap$compatHasInitialized = true; + } else { + eap$compatHasInitialized = false; + eap$compatDelayedInitTicks = Math.max(eap$compatDelayedInitTicks, 5); + try { + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + } catch (Throwable ignored) {} + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性频道链接初始化失败", e); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + eap$compatClientConnected = connected; + } + } + + @Override + public boolean eap$isWirelessConnected() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return false; + } + + try { + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return eap$compatClientConnected; + } else { + return eap$compatLink != null && eap$compatLink.isConnected(); + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("检查兼容性无线连接状态失败", e); + return false; + } + } + + @Override + public boolean eap$hasTickInitialized() { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return eap$compatHasInitialized; + } + return true; + } + + @Override + public void eap$setTickInitialized(boolean initialized) { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + eap$compatHasInitialized = initialized; + } + } + + @Override + public void eap$handleDelayedInit() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + + try { + // 仅服务端 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return; + } + if (!eap$compatHasInitialized) { + if (!mainNode.hasGridBooted()) { + if (eap$compatDelayedInitTicks > 0) { + eap$compatDelayedInitTicks--; + } + if (eap$compatDelayedInitTicks == 0) { + eap$compatDelayedInitTicks = 5; + try { + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + } catch (Throwable ignored) {} + } + } else { + eap$compatInitializeChannelLink(); + } + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性延迟初始化失败", e); + } + } + + @Inject(method = "onMainNodeStateChanged", at = @At("TAIL")) + private void eap$compatOnMainNodeStateChangedTail(CallbackInfo ci) { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + + try { + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatDelayedInitTicks = 10; + try { + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + } catch (Throwable ignored) {} + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性主节点状态变更处理失败", e); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java new file mode 100644 index 0000000..00b5c01 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java @@ -0,0 +1,27 @@ +package com.extendedae_plus.mixin.ae2.compat; + +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.compat.UpgradeSlotCompat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +/** + * PatternProviderLogicHost的兼容性Mixin + * 优先级设置为500,避免与appflux冲突 + */ +@Mixin(value = PatternProviderLogicHost.class, priority = 500, remap = false) +public interface PatternProviderLogicHostCompatMixin extends IUpgradeableObject { + @Shadow PatternProviderLogic getLogic(); + + @Override + default IUpgradeInventory getUpgrades() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return UpgradeInventories.empty(); + } + return ((IUpgradeableObject) this.getLogic()).getUpgrades(); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java new file mode 100644 index 0000000..eaee82c --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java @@ -0,0 +1,88 @@ +package com.extendedae_plus.mixin.ae2.compat; + +import appeng.api.upgrades.Upgrades; +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.implementations.PatternProviderScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.ToolboxPanel; +import appeng.client.gui.widgets.UpgradesPanel; +import appeng.core.localization.GuiText; +import appeng.menu.SlotSemantics; +import appeng.menu.implementations.PatternProviderMenu; +import com.extendedae_plus.compat.UpgradeSlotCompat; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; +import org.spongepowered.asm.mixin.Mixin; +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.util.ArrayList; +import java.util.List; + +/** + * PatternProviderScreen的兼容性Mixin + * 优先级设置为500,避免与appflux冲突 + */ +@Mixin(value = PatternProviderScreen.class, priority = 500, remap = false) +public abstract class PatternProviderScreenCompatMixin extends AEBaseScreen { + + @Inject(method = "", at = @At("TAIL")) + private void eap$initCompatUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + try { + // 检测是否应该添加升级面板 + if (UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) { + // 直接添加升级面板,不使用复杂的反射 + this.eap$addUpgradePanelDirect(menu, style); + } + } catch (Exception e) { + // 静默处理异常,确保不会因为升级功能导致崩溃 + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderScreen兼容性升级面板初始化失败", e); + } + } + + @Unique + private void eap$addUpgradePanelDirect(PatternProviderMenu menu, ScreenStyle style) { + try { + // 直接添加升级面板 + this.widgets.add("upgrades", new UpgradesPanel( + menu.getSlots(SlotSemantics.UPGRADE), + this::eap$getCompatibleUpgrades)); + + // 添加工具箱面板(如果菜单实现了兼容接口) + if (menu instanceof UpgradeSlotCompat.IUpgradeableMenuCompat compatMenu) { + var toolbox = compatMenu.getCompatToolbox(); + if (toolbox != null && toolbox.isPresent()) { + this.widgets.add("toolbox", new ToolboxPanel(style, toolbox.getName())); + } + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("直接添加升级面板失败", e); + } + } + + @Unique + private List eap$getCompatibleUpgrades() { + var list = new ArrayList(); + list.add(GuiText.CompatibleUpgrades.text()); + + try { + if (menu instanceof UpgradeSlotCompat.IUpgradeableMenuCompat compatMenu) { + var upgrades = compatMenu.getCompatUpgrades(); + if (upgrades != null) { + list.addAll(Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem())); + } + } + } catch (Exception e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("获取兼容升级列表失败", e); + } + + return list; + } + + // 构造函数,Mixin要求 + public PatternProviderScreenCompatMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) { + super(menu, playerInventory, title, style); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java index 9254e5d..47f27dc 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java @@ -10,12 +10,15 @@ import org.spongepowered.asm.mixin.Shadow; /** * 让 PatternProviderLogicHost 作为 IUpgradeableObject 的代理,菜单可从 host 获取升级槽。 */ -@Mixin(value = PatternProviderLogicHost.class, remap = false) +@Mixin(value = PatternProviderLogicHost.class, priority = 2000, remap = false) public interface PatternProviderLogicHostUpgradesMixin extends IUpgradeableObject { @Shadow PatternProviderLogic getLogic(); @Override default IUpgradeInventory getUpgrades() { + if (!com.extendedae_plus.compat.UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return appeng.api.upgrades.UpgradeInventories.empty(); + } return ((IUpgradeableObject) this.getLogic()).getUpgrades(); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java index 1a802cd..59f06c9 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java @@ -8,6 +8,7 @@ import appeng.api.upgrades.UpgradeInventories; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.compat.UpgradeSlotCompat; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.wireless.WirelessSlaveLink; import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; @@ -29,7 +30,7 @@ import java.util.List; * 为 PatternProviderLogic 注入升级槽,实现 IUpgradeableObject。 * 仅负责升级槽的持久化/掉落/清空与初始化,不改变原有逻辑。 */ -@Mixin(value = PatternProviderLogic.class, remap = false) +@Mixin(value = PatternProviderLogic.class, priority = 2000, remap = false) public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge { @Unique private IUpgradeInventory eap$upgrades = UpgradeInventories.empty(); @@ -75,16 +76,27 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO @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) { + // 只有在应该启用升级卡槽时才初始化 + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + this.eap$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::eap$onUpgradesChanged); } @Inject(method = "writeToNBT", at = @At("TAIL")) private void eap$saveUpgrades(CompoundTag tag, CallbackInfo ci) { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } this.eap$upgrades.writeToNBT(tag, "upgrades"); } @Inject(method = "readFromNBT", at = @At("TAIL")) private void eap$loadUpgrades(CompoundTag tag, CallbackInfo ci) { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } this.eap$upgrades.readFromNBT(tag, "upgrades"); // 从 NBT 加载后,重置并尝试初始化(可能刚进入世界) eap$lastChannel = -1; @@ -94,6 +106,9 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO @Inject(method = "addDrops", at = @At("TAIL")) private void eap$dropUpgrades(List drops, CallbackInfo ci) { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } for (var stack : this.eap$upgrades) { if (!stack.isEmpty()) { drops.add(stack); @@ -103,6 +118,9 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO @Inject(method = "clearContent", at = @At("TAIL")) private void eap$clearUpgrades(CallbackInfo ci) { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } this.eap$upgrades.clear(); } @@ -115,6 +133,9 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO @Override public IUpgradeInventory getUpgrades() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return UpgradeInventories.empty(); + } return this.eap$upgrades; } 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 0cda3ae..c5ed6d7 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.AEBaseMenu; import appeng.menu.ToolboxMenu; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.bridge.IUpgradableMenu; +import com.extendedae_plus.compat.UpgradeSlotCompat; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; import org.spongepowered.asm.mixin.Final; @@ -18,7 +19,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(value = PatternProviderMenu.class, remap = false) +@Mixin(value = PatternProviderMenu.class, priority = 2000, remap = false) public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implements IUpgradableMenu { @Final @Shadow protected PatternProviderLogic logic; @@ -29,17 +30,28 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) private void eap$initUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { + // 只有在应该启用升级卡槽时才初始化 + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return; + } + this.eap$toolbox = new ToolboxMenu(this); this.setupUpgrades(((IUpgradeableObject) host).getUpgrades()); } @Override public ToolboxMenu getToolbox() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return null; + } return this.eap$toolbox; } @Override public IUpgradeInventory getUpgrades() { + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return appeng.api.upgrades.UpgradeInventories.empty(); + } return ((IUpgradeableObject) this.logic).getUpgrades(); } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index ab3d360..5c81819 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -20,7 +20,7 @@ "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", - "ae2.client.gui.PatternProviderScreenUpgradesMixin", + "ae2.compat.PatternProviderScreenCompatMixin", "ae2.client.gui.SlotGridLayoutMixin", "ae2.menu.CraftConfirmMenuGoBackMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", @@ -63,9 +63,9 @@ "ae2.menu.PatternEncodingTermMenuMixin", "ae2.menu.PatternProviderMenuAdvancedMixin", "ae2.menu.PatternProviderMenuDoublingMixin", - "ae2.helpers.patternprovider.PatternProviderLogicUpgradesMixin", - "ae2.helpers.patternprovider.PatternProviderLogicHostUpgradesMixin", - "ae2.menu.PatternProviderMenuUpgradesMixin", + "ae2.compat.PatternProviderLogicCompatMixin", + "ae2.compat.PatternProviderLogicHostCompatMixin", + "ae2.compat.PatternProviderCompatMixin", "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2.parts.AEBasePartClientSyncMixin", "ae2.parts.automation.IOBusPartChannelCardMixin", @@ -84,5 +84,6 @@ "injectors": { "defaultRequire": 1 }, + "plugin": "com.extendedae_plus.mixin.MixinConditions", "priority": 1000 }