diff --git a/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java new file mode 100644 index 0000000..470ccd5 --- /dev/null +++ b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java @@ -0,0 +1,12 @@ +package com.extendedae_plus.bridge; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.menu.ToolboxMenu; + +public interface IUpgradableMenu { + ToolboxMenu getToolbox(); + IUpgradeInventory getUpgrades(); + default boolean hasUpgrade(net.minecraft.world.level.ItemLike upgradeCard) { + return getUpgrades().isInstalled(upgradeCard); + } +} 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 new file mode 100644 index 0000000..5e94289 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java @@ -0,0 +1,48 @@ +package com.extendedae_plus.mixin.ae2.client.gui; + +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.bridge.IUpgradableMenu; +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; + +@Mixin(value = PatternProviderScreen.class, remap = false) +public abstract class PatternProviderScreenUpgradesMixin extends AEBaseScreen { + + @Inject(method = "", at = @At("TAIL")) + private void extendedae_plus$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + this.widgets.add("upgrades", new UpgradesPanel( + menu.getSlots(SlotSemantics.UPGRADE), + this::extendedae_plus$getCompatibleUpgrades)); + if (((IUpgradableMenu) menu).getToolbox().isPresent()) { + this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName())); + } + } + + @Unique + private List extendedae_plus$getCompatibleUpgrades() { + var list = new ArrayList(); + list.add(GuiText.CompatibleUpgrades.text()); + list.addAll(Upgrades.getTooltipLinesForMachine(((IUpgradableMenu) menu).getUpgrades().getUpgradableItem())); + return list; + } + + public PatternProviderScreenUpgradesMixin(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 new file mode 100644 index 0000000..9254e5d --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java @@ -0,0 +1,21 @@ +package com.extendedae_plus.mixin.ae2.helpers.patternprovider; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.helpers.patternprovider.PatternProviderLogic; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +/** + * 让 PatternProviderLogicHost 作为 IUpgradeableObject 的代理,菜单可从 host 获取升级槽。 + */ +@Mixin(value = PatternProviderLogicHost.class, remap = false) +public interface PatternProviderLogicHostUpgradesMixin extends IUpgradeableObject { + @Shadow PatternProviderLogic getLogic(); + + @Override + default IUpgradeInventory getUpgrades() { + return ((IUpgradeableObject) this.getLogic()).getUpgrades(); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java new file mode 100644 index 0000000..8baebff --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java @@ -0,0 +1,30 @@ +package com.extendedae_plus.mixin.ae2.helpers.patternprovider; + +import appeng.helpers.patternprovider.PatternProviderLogic; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +/** + * 注入到 PatternProviderLogic.Ticker 的每tick回调,驱动无线链接状态更新。 + */ +@Mixin(targets = "appeng.helpers.patternprovider.PatternProviderLogic$Ticker", remap = false) +public abstract class PatternProviderLogicTickerMixin { + + // Mixin 访问内部类的外部引用字段(javac 生成名 this$0) + @Shadow(remap = false) + @Final + private PatternProviderLogic this$0; + + @Inject(method = "tickingRequest", at = @At("TAIL")) + private void extendedae_plus$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall, + CallbackInfoReturnable cir) { + if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { + bridge.extendedae_plus$updateWirelessLink(); + } + } +} 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 new file mode 100644 index 0000000..82e5bb0 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java @@ -0,0 +1,112 @@ +package com.extendedae_plus.mixin.ae2.helpers.patternprovider; + +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.init.ModItems; +import com.extendedae_plus.wireless.WirelessSlaveLink; +import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +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 net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; + +import java.util.List; + +/** + * 为 PatternProviderLogic 注入升级槽,实现 IUpgradeableObject。 + * 仅负责升级槽的持久化/掉落/清空与初始化,不改变原有逻辑。 + */ +@Mixin(value = PatternProviderLogic.class, remap = false) +public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge { + @Unique + private IUpgradeInventory extendedae_plus$upgrades = UpgradeInventories.empty(); + + @Unique + private WirelessSlaveLink extendedae_plus$link; + + @Final + @Shadow + private PatternProviderLogicHost host; + + @Final + @Shadow + private IManagedGridNode mainNode; + + @Final + @Shadow + private IActionSource actionSource; + + @Unique + private void extendedae_plus$onUpgradesChanged() { + this.host.saveChanges(); + // 读取频道卡,更新无线链接频率 + long channel = 0L; + for (var stack : this.extendedae_plus$upgrades) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + break; + } + } + if (extendedae_plus$link == null) { + var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode()); + extendedae_plus$link = new WirelessSlaveLink(endpoint); + } + extendedae_plus$link.setFrequency(channel); + extendedae_plus$link.updateStatus(); + } + + @Override + public IUpgradeInventory getUpgrades() { + return this.extendedae_plus$upgrades; + } + + @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V", + at = @At("TAIL")) + private void extendedae_plus$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { + this.extendedae_plus$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::extendedae_plus$onUpgradesChanged); + } + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void extendedae_plus$saveUpgrades(CompoundTag tag, CallbackInfo ci) { + this.extendedae_plus$upgrades.writeToNBT(tag, "upgrades"); + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void extendedae_plus$loadUpgrades(CompoundTag tag, CallbackInfo ci) { + this.extendedae_plus$upgrades.readFromNBT(tag, "upgrades"); + } + + @Inject(method = "addDrops", at = @At("TAIL")) + private void extendedae_plus$dropUpgrades(List drops, CallbackInfo ci) { + for (var stack : this.extendedae_plus$upgrades) { + if (!stack.isEmpty()) { + drops.add(stack); + } + } + } + + @Inject(method = "clearContent", at = @At("TAIL")) + private void extendedae_plus$clearUpgrades(CallbackInfo ci) { + this.extendedae_plus$upgrades.clear(); + } + + @Override + public void extendedae_plus$updateWirelessLink() { + if (extendedae_plus$link != null) { + extendedae_plus$link.updateStatus(); + } + } +} 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 new file mode 100644 index 0000000..514b523 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java @@ -0,0 +1,49 @@ +package com.extendedae_plus.mixin.ae2.menu; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.helpers.patternprovider.PatternProviderLogic; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import appeng.menu.AEBaseMenu; +import appeng.menu.ToolboxMenu; +import appeng.menu.implementations.PatternProviderMenu; +import com.extendedae_plus.bridge.IUpgradableMenu; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +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; + +@Mixin(value = PatternProviderMenu.class, remap = false) +public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implements IUpgradableMenu { + @Final + @Shadow protected PatternProviderLogic logic; + + @Unique + private ToolboxMenu extendedae_plus$toolbox; + + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", + at = @At("TAIL")) + private void extendedae_plus$initUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { + this.extendedae_plus$toolbox = new ToolboxMenu(this); + this.setupUpgrades(((IUpgradeableObject) host).getUpgrades()); + } + + @Override + public ToolboxMenu getToolbox() { + return this.extendedae_plus$toolbox; + } + + @Override + public IUpgradeInventory getUpgrades() { + return ((IUpgradeableObject) this.logic).getUpgrades(); + } + + public PatternProviderMenuUpgradesMixin(MenuType menuType, int id, Inventory playerInventory, Object host) { + super(menuType, id, playerInventory, host); + } +} diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java new file mode 100644 index 0000000..84cd191 --- /dev/null +++ b/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java @@ -0,0 +1,49 @@ +package com.extendedae_plus.wireless.endpoint; + +import appeng.api.networking.IGridNode; +import com.extendedae_plus.wireless.IWirelessEndpoint; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * 通用 IWirelessEndpoint:通过提供方块实体与节点的 Supplier 实现。 + */ +public class GenericNodeEndpointImpl implements IWirelessEndpoint { + private final Supplier blockEntitySupplier; + private final Supplier nodeSupplier; + + public GenericNodeEndpointImpl(Supplier blockEntitySupplier, Supplier nodeSupplier) { + this.blockEntitySupplier = Objects.requireNonNull(blockEntitySupplier); + this.nodeSupplier = Objects.requireNonNull(nodeSupplier); + } + + @Override + public ServerLevel getServerLevel() { + var be = blockEntitySupplier.get(); + if (be == null) return null; + Level lvl = be.getLevel(); + return (lvl instanceof ServerLevel) ? (ServerLevel) lvl : null; + } + + @Override + public BlockPos getBlockPos() { + var be = blockEntitySupplier.get(); + return be != null ? be.getBlockPos() : BlockPos.ZERO; + } + + @Override + public IGridNode getGridNode() { + return nodeSupplier.get(); + } + + @Override + public boolean isEndpointRemoved() { + var be = blockEntitySupplier.get(); + return be == null || be.isRemoved(); + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index a856a94..b584236 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -20,6 +20,7 @@ "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", + "ae2.client.gui.PatternProviderScreenUpgradesMixin", "ae2.client.gui.SlotGridLayoutMixin", "ae2.menu.CraftConfirmMenuGoBackMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", @@ -62,6 +63,10 @@ "ae2.menu.PatternEncodingTermMenuMixin", "ae2.menu.PatternProviderMenuAdvancedMixin", "ae2.menu.PatternProviderMenuDoublingMixin", + "ae2.helpers.patternprovider.PatternProviderLogicUpgradesMixin", + "ae2.helpers.patternprovider.PatternProviderLogicHostUpgradesMixin", + "ae2.menu.PatternProviderMenuUpgradesMixin", + "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2WTlib.ContainerUWirelessExPatternTerminalMixin", "extendedae.common.PartExPatternProviderMixin", "extendedae.common.TileExPatternProviderMixin",