From c86370caf9d93e27f5411cbcb3db46f9c67bcbd5 Mon Sep 17 00:00:00 2001 From: lc-1337 <62835225+lc-1337@users.noreply.github.com> Date: Tue, 1 Jul 2025 21:40:42 +0300 Subject: [PATCH] implement pins (#315) Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com> Co-authored-by: Martin Robertz --- .../github/client/gui/base/FCGuiMonitor.java | 107 +++++++++++++++- .../container/base/FCContainerMonitor.java | 115 +++++++++++++++++- .../parts/base/FCFluidEncodeTerminal.java | 20 ++- .../inventory/item/BaseWirelessInventory.java | 20 ++- 4 files changed, 253 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/glodblock/github/client/gui/base/FCGuiMonitor.java b/src/main/java/com/glodblock/github/client/gui/base/FCGuiMonitor.java index ba9f372d..8d681df2 100644 --- a/src/main/java/com/glodblock/github/client/gui/base/FCGuiMonitor.java +++ b/src/main/java/com/glodblock/github/client/gui/base/FCGuiMonitor.java @@ -17,6 +17,7 @@ import org.lwjgl.input.Mouse; import com.glodblock.github.FluidCraft; import com.glodblock.github.client.gui.FCGuiTextField; +import com.glodblock.github.client.gui.GuiFluidMonitor; import com.glodblock.github.client.gui.GuiItemMonitor; import com.glodblock.github.client.gui.container.base.FCContainerMonitor; import com.glodblock.github.inventory.InventoryHandler; @@ -26,12 +27,14 @@ import com.glodblock.github.util.Ae2ReflectClient; import com.glodblock.github.util.ModAndClassUtil; import appeng.api.config.CraftingStatus; +import appeng.api.config.PinsState; import appeng.api.config.SearchBoxMode; import appeng.api.config.Settings; import appeng.api.config.TerminalStyle; import appeng.api.config.YesNo; import appeng.api.implementations.tiles.IViewCellStorage; import appeng.api.storage.ITerminalHost; +import appeng.api.storage.ITerminalPins; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IDisplayRepo; @@ -43,6 +46,8 @@ import appeng.client.gui.widgets.GuiTabButton; import appeng.client.gui.widgets.IDropToFillTextField; import appeng.client.gui.widgets.ISortSource; import appeng.client.me.InternalSlotME; +import appeng.client.me.ItemRepo; +import appeng.client.me.PinSlotME; import appeng.client.me.SlotDisconnected; import appeng.client.me.SlotME; import appeng.container.AEBaseContainer; @@ -60,7 +65,9 @@ import appeng.core.localization.ButtonToolTips; import appeng.core.localization.GuiText; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketInventoryAction; +import appeng.core.sync.packets.PacketPinsUpdate; import appeng.core.sync.packets.PacketValueConfig; +import appeng.helpers.IPinsHandler; import appeng.helpers.InventoryAction; import appeng.integration.IntegrationRegistry; import appeng.integration.IntegrationType; @@ -72,7 +79,7 @@ import codechicken.nei.util.TextHistory; // TODO why is this copy pasting all the UI code from AE2 instead of reusing it ??? public abstract class FCGuiMonitor> extends FCBaseMEGui - implements ISortSource, IConfigManagerHost, IDropToFillTextField { + implements ISortSource, IConfigManagerHost, IDropToFillTextField, IPinsHandler { public static int craftingGridOffsetX; public static int craftingGridOffsetY; @@ -102,7 +109,10 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui protected GuiImgButton terminalStyleBox; protected GuiImgButton searchStringSave; protected GuiImgButton typeFilter; + protected GuiImgButton pinsStateButton; protected boolean hasShiftKeyDown = false; + private PinsState pinsState; + public final boolean hasPinHost; @SuppressWarnings("unchecked") public FCGuiMonitor(final InventoryPlayer inventoryPlayer, final ITerminalHost te, final FCContainerMonitor c) { @@ -115,6 +125,8 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui this.configSrc = ((IConfigurableObject) this.inventorySlots).getConfigManager(); (this.monitorableContainer = (FCContainerMonitor) this.inventorySlots).setGui(this); this.viewCell = te instanceof IViewCellStorage; + pinsState = (PinsState) configSrc.getSetting(Settings.PINS_STATE); + hasPinHost = te instanceof ITerminalPins && !(this instanceof GuiFluidMonitor); } public int getOffsetY() { @@ -131,7 +143,7 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui this.getScrollBar().setTop(18).setLeft(175).setHeight(this.rows * 18 - 2); this.getScrollBar().setRange( 0, - (this.repo.size() + this.perRow - 1) / this.perRow - this.rows, + (this.repo.size() + pinsState.ordinal() * 9 + this.perRow - 1) / this.perRow - this.rows, Math.max(1, this.rows / 6)); } @@ -151,6 +163,15 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui AEConfig.instance.settings.putSetting(iBtn.getSetting(), next); } else if (ModAndClassUtil.isSaveText && btn == this.searchStringSave) { AEConfig.instance.preserveSearchBar = next == YesNo.YES; + } else if (btn == this.pinsStateButton) { + try { + if (next.ordinal() >= rows) return; // ignore to avoid hiding terminal inventory + + final PacketPinsUpdate p = new PacketPinsUpdate((PinsState) next); + NetworkHandler.instance.sendToServer(p); + } catch (final IOException e) { + AELog.debug(e); + } } else { try { NetworkHandler.instance @@ -169,6 +190,19 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui super.actionPerformed(btn); } + private void adjustPinsSize() { + final int pinMaxSize = rows - 1; + if (pinsState.ordinal() <= pinMaxSize) return; + + try { + PinsState newState = PinsState.fromOrdinal(pinMaxSize); + final PacketPinsUpdate p = new PacketPinsUpdate(newState); + NetworkHandler.instance.sendToServer(p); + } catch (final IOException e) { + AELog.debug(e); + } + } + @Override public void initGui() { Keyboard.enableRepeatEvents(true); @@ -199,10 +233,26 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui } this.getMeSlots().clear(); - for (int y = 0; y < this.rows; y++) { + + // make sure we have space at least for one row of normal slots, because pins not adjusted by scroll bar + adjustPinsSize(); + + int pinsRows = pinsState.ordinal(); + for (int y = 0; y < pinsRows; y++) { for (int x = 0; x < this.perRow; x++) { this.getMeSlots() - .add(new InternalSlotME(this.repo, x + y * this.perRow, this.offsetX + x * 18, 18 + y * 18)); + .add(new PinSlotME(this.repo, x + y * this.perRow, this.offsetX + x * 18, y * 18 + 18)); + } + } + + for (int y = 0; y < this.rows - pinsRows; y++) { + for (int x = 0; x < this.perRow; x++) { + this.getMeSlots().add( + new InternalSlotME( + this.repo, + x + y * this.perRow, + this.offsetX + x * 18, + 18 + y * 18 + pinsRows * 18)); } } @@ -224,6 +274,7 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui this.offsetY = this.guiTop + 8; + buttonList.clear(); if (this.customSortOrder) { this.buttonList.add( this.SortByBox = new GuiImgButton( @@ -323,6 +374,15 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui } } + if (hasPinHost) { + this.buttonList.add( + this.pinsStateButton = new GuiImgButton( + this.guiLeft + 177, + this.guiTop + 18 + (rows * 18) + 26, + Settings.PINS_STATE, + configSrc.getSetting(Settings.PINS_STATE))); + } + final Enum setting = AEConfig.instance.settings.getSetting(Settings.SEARCH_MODE); this.searchField.setFocused(SearchBoxMode.AUTOSEARCH == setting || SearchBoxMode.NEI_AUTOSEARCH == setting); @@ -527,13 +587,22 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui return; } - if (slot instanceof SlotME) { + if (slot instanceof SlotME sme) { InventoryAction action = null; IAEItemStack stack = null; switch (mouseButton) { case 0: // pickup / set-down. action = ctrlDown == 1 ? InventoryAction.SPLIT_OR_PLACE_SINGLE : InventoryAction.PICKUP_OR_SET_DOWN; - stack = ((SlotME) slot).getAEStack(); + if (sme.isPin() && player.inventory.getItemStack() != null) { + final PacketInventoryAction p = new PacketInventoryAction( + InventoryAction.SET_PIN, + sme.getPinIndex(), + 0); + NetworkHandler.instance.sendToServer(p); + return; + } + + stack = sme.getAEStack(); if (stack != null && action == InventoryAction.PICKUP_OR_SET_DOWN && stack.getStackSize() == 0 && player.inventory.getItemStack() == null) { @@ -541,6 +610,15 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui } break; case 1: + if (sme.isPin() && ctrlDown == 1) { // clear pin + final PacketInventoryAction p = new PacketInventoryAction( + InventoryAction.SET_PIN, + sme.getPinIndex(), + -1); + NetworkHandler.instance.sendToServer(p); + return; + } + action = ctrlDown == 1 ? InventoryAction.PICKUP_SINGLE : InventoryAction.SHIFT_CLICK; stack = ((SlotME) slot).getAEStack(); break; @@ -721,6 +799,13 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui this.typeFilter.set(this.configSrc.getSetting(Settings.TYPE_FILTER)); } + if (this.pinsStateButton != null) { + pinsState = (PinsState) this.configSrc.getSetting(Settings.PINS_STATE); + this.pinsStateButton.set(pinsState); + + initGui(); + } + this.repo.updateView(); } @@ -786,4 +871,14 @@ public abstract class FCGuiMonitor> extends FCBaseMEGui public boolean isOverTextField(int mousex, int mousey) { return searchField.isMouseIn(mousex, mousey); } + + @Override + public void setAEPins(IAEItemStack[] pins) { + if (repo instanceof ItemRepo) repo.setAEPins(pins); + } + + @Override + public void setPinsState(PinsState state) { + configSrc.putSetting(Settings.PINS_STATE, state); + } } diff --git a/src/main/java/com/glodblock/github/client/gui/container/base/FCContainerMonitor.java b/src/main/java/com/glodblock/github/client/gui/container/base/FCContainerMonitor.java index d4437b01..bb50853f 100644 --- a/src/main/java/com/glodblock/github/client/gui/container/base/FCContainerMonitor.java +++ b/src/main/java/com/glodblock/github/client/gui/container/base/FCContainerMonitor.java @@ -1,6 +1,8 @@ package com.glodblock.github.client.gui.container.base; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import javax.annotation.Nonnull; @@ -10,10 +12,15 @@ import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.ICrafting; import net.minecraft.item.ItemStack; +import com.glodblock.github.inventory.item.BaseWirelessInventory; import com.glodblock.github.inventory.item.IWirelessTerminal; import com.glodblock.github.network.SPacketMEUpdateBuffer; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import appeng.api.config.Actionable; +import appeng.api.config.CraftingAllow; +import appeng.api.config.PinsState; import appeng.api.config.PowerMultiplier; import appeng.api.config.SecurityPermissions; import appeng.api.config.Settings; @@ -22,10 +29,16 @@ import appeng.api.config.SortOrder; import appeng.api.config.TypeFilter; import appeng.api.config.ViewItems; import appeng.api.networking.IGridNode; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingGrid; import appeng.api.networking.energy.IEnergyGrid; +import appeng.api.networking.security.PlayerSource; import appeng.api.storage.IMEMonitor; import appeng.api.storage.IMEMonitorHandlerReceiver; import appeng.api.storage.ITerminalHost; +import appeng.api.storage.ITerminalPins; +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.api.util.IConfigManager; import appeng.api.util.IConfigurableObject; @@ -34,18 +47,22 @@ import appeng.container.slot.SlotRestrictedInput; import appeng.core.AELog; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketValueConfig; +import appeng.helpers.IPinsHandler; import appeng.helpers.WirelessTerminalGuiObject; +import appeng.items.contents.PinsHandler; import appeng.util.ConfigManager; import appeng.util.IConfigManagerHost; import appeng.util.Platform; public abstract class FCContainerMonitor> extends FCBaseContainer - implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver { + implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver, IPinsHandler { protected final SlotRestrictedInput[] cellView = new SlotRestrictedInput[5]; protected final IConfigManager clientCM; protected final ITerminalHost host; + protected PinsHandler pinsHandler = null; + @GuiSync(99) public boolean canAccessViewCells = false; @@ -70,6 +87,14 @@ public abstract class FCContainerMonitor> extends FCBaseCo this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL); this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING); this.clientCM.registerSetting(Settings.TYPE_FILTER, TypeFilter.ALL); + this.clientCM.registerSetting(Settings.PINS_STATE, PinsState.DISABLED); // use for GUI + + if (Platform.isServer()) { + if (monitorable instanceof ITerminalPins t && !(monitorable instanceof BaseWirelessInventory bwi + && bwi.getChannel() == StorageChannel.FLUIDS)) { + pinsHandler = t.getPinsHandler(ip.player); + } + } } public IMEMonitor getMonitor() { @@ -105,6 +130,16 @@ public abstract class FCContainerMonitor> extends FCBaseCo } } } + + if (pinsHandler != null) { + if (clientCM.getSetting(Settings.PINS_STATE) != pinsHandler.getPinsState()) { + this.clientCM.putSetting(Settings.PINS_STATE, pinsHandler.getPinsState()); + updatePins(true); + } else { + updatePins(false); + } + } + processItemList(); this.updatePowerStatus(); final boolean oldAccessible = this.canAccessViewCells; @@ -122,6 +157,84 @@ public abstract class FCContainerMonitor> extends FCBaseCo } } + private int lastUpdate = 0; + + public void updatePins(boolean forceUpdate) { + if (pinsHandler == null || !(host instanceof ITerminalPins itp)) return; + + boolean isActive = pinsHandler.getPinsState() != PinsState.DISABLED; + ++lastUpdate; + if (!forceUpdate && lastUpdate <= 20) return; + lastUpdate = 0; + if (isActive) { + final ICraftingGrid cc = itp.getGrid().getCache(ICraftingGrid.class); + final ImmutableList cpuList = cc.getCpus().asList(); + + List craftedItems = new ArrayList<>(); + + // fetch the first available crafting output + for (int i = 0; i < cpuList.size(); i++) { + ICraftingCPU cpu = cpuList.get(i); + if (cpu.getCraftingAllowMode() != CraftingAllow.ONLY_NONPLAYER && cpu.getFinalOutput() != null + && cpu.getCurrentJobSource() instanceof PlayerSource src + && src.player == pinsHandler.getPlayer()) { + if (craftedItems.contains(cpu.getFinalOutput())) { + continue; // skip if already added + } + if (cpu.isBusy()) craftedItems.add(0, cpu.getFinalOutput().copy()); + else craftedItems.add(cpu.getFinalOutput().copy()); + } + } + + pinsHandler.addItemsToPins(craftedItems); + } + pinsHandler.update(forceUpdate); + onListUpdate(); // notify the repo that the pins have changed + } + + @Override + public void setPin(ItemStack is, int idx) { + if (pinsHandler == null || !(host instanceof ITerminalPins itp)) return; + + if (is == null) { + final ICraftingGrid cc = itp.getGrid().getCache(ICraftingGrid.class); + final ImmutableSet cpuSet = cc.getCpus(); + for (ICraftingCPU cpu : cpuSet) { + if (cpu.getCraftingAllowMode() != CraftingAllow.ONLY_NONPLAYER && cpu.getFinalOutput() != null + && cpu.getFinalOutput().isSameType(getPin(idx))) { + if (!cpu.isBusy()) { + cpu.resetFinalOutput(); + } else { + return; + } + } + } + } + pinsHandler.setPin(idx, is); + updatePins(true); + } + + @Override + public ItemStack getPin(int idx) { + if (pinsHandler == null) return null; + return pinsHandler.getPin(idx); + } + + public void setPinsState(PinsState pinsState) { + if (pinsHandler == null) return; + clientCM.putSetting(Settings.PINS_STATE, pinsState); + pinsHandler.setPinsState(pinsState); + updatePins(true); + } + + public PinsState getPinsState() { + return pinsHandler != null ? pinsHandler.getPinsState() : PinsState.DISABLED; + } + + public PinsHandler getPinsHandler() { + return pinsHandler; + } + protected abstract void processItemList(); protected boolean isInvalid() { diff --git a/src/main/java/com/glodblock/github/common/parts/base/FCFluidEncodeTerminal.java b/src/main/java/com/glodblock/github/common/parts/base/FCFluidEncodeTerminal.java index 90ab4e31..5ae403d0 100644 --- a/src/main/java/com/glodblock/github/common/parts/base/FCFluidEncodeTerminal.java +++ b/src/main/java/com/glodblock/github/common/parts/base/FCFluidEncodeTerminal.java @@ -3,6 +3,7 @@ package com.glodblock.github.common.parts.base; import java.util.ArrayList; import java.util.List; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -10,14 +11,19 @@ import net.minecraft.nbt.NBTTagCompound; import com.glodblock.github.common.item.ItemFluidPacket; import com.glodblock.github.inventory.item.IItemPatternTerminal; +import appeng.api.networking.IGrid; +import appeng.api.storage.ITerminalPins; import appeng.api.storage.data.IAEItemStack; +import appeng.items.contents.PinsHandler; +import appeng.items.contents.PinsHolder; import appeng.tile.inventory.AppEngInternalInventory; -public abstract class FCFluidEncodeTerminal extends FCPart implements IItemPatternTerminal { +public abstract class FCFluidEncodeTerminal extends FCPart implements IItemPatternTerminal, ITerminalPins { protected AppEngInternalInventory crafting; protected AppEngInternalInventory output; protected final AppEngInternalInventory pattern = new AppEngInternalInventory(this, 2); + private final PinsHolder pinsInv = new PinsHolder(this); protected boolean craftingMode = true; protected boolean substitute = false; @@ -56,6 +62,7 @@ public abstract class FCFluidEncodeTerminal extends FCPart implements IItemPatte this.pattern.readFromNBT(data, "pattern"); this.output.readFromNBT(data, "outputList"); this.crafting.readFromNBT(data, "craftingGrid"); + pinsInv.readFromNBT(data, "pins"); } @Override @@ -72,6 +79,7 @@ public abstract class FCFluidEncodeTerminal extends FCPart implements IItemPatte this.pattern.writeToNBT(data, "pattern"); this.output.writeToNBT(data, "outputList"); this.crafting.writeToNBT(data, "craftingGrid"); + pinsInv.writeToNBT(data, "pins"); } @Override @@ -217,4 +225,14 @@ public abstract class FCFluidEncodeTerminal extends FCPart implements IItemPatte public void setAutoFillPattern(boolean canFill) { this.autoFillPattern = canFill; } + + @Override + public PinsHandler getPinsHandler(EntityPlayer player) { + return pinsInv.getHandler(player); + } + + @Override + public IGrid getGrid() { + return getGridNode().getGrid(); + } } diff --git a/src/main/java/com/glodblock/github/inventory/item/BaseWirelessInventory.java b/src/main/java/com/glodblock/github/inventory/item/BaseWirelessInventory.java index 6a41bc7c..9c46b446 100644 --- a/src/main/java/com/glodblock/github/inventory/item/BaseWirelessInventory.java +++ b/src/main/java/com/glodblock/github/inventory/item/BaseWirelessInventory.java @@ -16,20 +16,25 @@ import com.glodblock.github.util.Util; import appeng.api.config.Actionable; import appeng.api.config.PowerMultiplier; import appeng.api.implementations.items.IAEItemPowerStorage; +import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; import appeng.api.networking.security.PlayerSource; import appeng.api.networking.storage.IStorageGrid; +import appeng.api.storage.ITerminalPins; import appeng.api.storage.MEMonitorHandler; import appeng.api.storage.StorageChannel; import appeng.api.storage.data.IAEFluidStack; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.api.util.AECableType; +import appeng.items.contents.PinsHandler; +import appeng.items.contents.PinsHolder; import appeng.items.tools.powered.ToolWirelessTerminal; import appeng.me.storage.NullInventory; import appeng.util.Platform; -public abstract class BaseWirelessInventory extends MEMonitorHandler implements IWirelessTerminal, IWirelessExtendCard { +public abstract class BaseWirelessInventory extends MEMonitorHandler + implements IWirelessTerminal, IWirelessExtendCard, ITerminalPins { protected final ItemStack target; protected final IAEItemPowerStorage ips; @@ -42,6 +47,7 @@ public abstract class BaseWirelessInventory extends MEMonitorHandler implements protected WirelessMagnet.Mode magnetMode = WirelessMagnet.Mode.Off; protected EntityPlayer player; protected boolean restock; + private final PinsHolder pinsInv; @SuppressWarnings("unchecked") public BaseWirelessInventory(final ItemStack is, final int slot, IGridNode gridNode, EntityPlayer player, @@ -54,6 +60,7 @@ public abstract class BaseWirelessInventory extends MEMonitorHandler implements this.inventorySlot = slot; this.channel = channel; this.source = new PlayerSource(player, this); + pinsInv = new PinsHolder(is); this.readFromNBT(); } @@ -68,6 +75,7 @@ public abstract class BaseWirelessInventory extends MEMonitorHandler implements this.inventorySlot = slot; this.channel = channel; this.source = new PlayerSource(player, this); + pinsInv = new PinsHolder(is); this.readFromNBT(); } @@ -184,4 +192,14 @@ public abstract class BaseWirelessInventory extends MEMonitorHandler implements data.setBoolean(restockItems, this.restock); data.setInteger(modeKey, this.getMagnetCardMode().ordinal()); } + + @Override + public PinsHandler getPinsHandler(EntityPlayer player) { + return pinsInv.getHandler(player); + } + + @Override + public IGrid getGrid() { + return grid != null ? grid.getGrid() : null; + } }