From e14c92aad7dc01e0a6e58af40150dc9f839cb7c1 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Tue, 24 Mar 2026 17:58:42 +0800 Subject: [PATCH 01/21] refactor AE2 grid node machine trait --- .../common/machine/electric/HullMachine.java | 59 +----------- .../feature/IGridConnectedMachine.java | 48 +--------- .../ae2/machine/trait/GridNodeHolder.java | 96 ------------------- .../ae2/machine/trait/GridNodeHost.java | 88 +++++++++++++++++ .../ae2/machine/trait/GridNodeHostTrait.java | 51 ---------- .../ae2/utils/MachineNodeListener.java | 23 +++++ .../utils/SerializableManagedGridNode.java | 27 ------ 7 files changed, 120 insertions(+), 272 deletions(-) delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/SerializableManagedGridNode.java diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java index 4f5c9d06f9e..98dd8b36091 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java @@ -7,21 +7,17 @@ import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredPartMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHostTrait; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.TickTask; -import net.minecraft.server.level.ServerLevel; -import appeng.me.helpers.IGridConnectedBlockEntity; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @@ -32,14 +28,14 @@ public class HullMachine extends TieredPartMachine implements IMonitorComponent protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(HullMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); - private final Object gridNodeHost; + private final @Nullable MachineTrait gridNodeHost; @Persisted protected NotifiableEnergyContainer energyContainer; public HullMachine(IMachineBlockEntity holder, int tier) { super(holder, tier); if (GTCEu.Mods.isAE2Loaded()) { - this.gridNodeHost = new GridNodeHostTrait(this); + this.gridNodeHost = new GridNodeHost(this); } else { this.gridNodeHost = null; } @@ -52,56 +48,11 @@ protected void reinitializeEnergyContainer() { this.energyContainer.setSideOutputCondition(s -> s == getFrontFacing()); } - @Override - public void onLoad() { - super.onLoad(); - if (GTCEu.Mods.isAE2Loaded() && gridNodeHost instanceof GridNodeHostTrait connectedBlockEntity && - getLevel() instanceof ServerLevel level) { - level.getServer().tell(new TickTask(0, connectedBlockEntity::init)); - } - } - - @Override - public void onUnload() { - super.onUnload(); - if (GTCEu.Mods.isAE2Loaded() && gridNodeHost instanceof GridNodeHostTrait connectedBlockEntity) { - connectedBlockEntity.getMainNode().destroy(); - } - } - - @Override - public void setFrontFacing(Direction facing) { - super.setFrontFacing(facing); - if (isFacingValid(facing)) { - if (GTCEu.Mods.isAE2Loaded() && gridNodeHost instanceof GridNodeHostTrait connectedBlockEntity) { - connectedBlockEntity.init(); - } - } - } - @Override public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - @Override - public void saveCustomPersistedData(@NotNull CompoundTag tag, boolean forDrop) { - super.saveCustomPersistedData(tag, forDrop); - if (GTCEu.Mods.isAE2Loaded() && gridNodeHost instanceof IGridConnectedBlockEntity connectedBlockEntity) { - CompoundTag nbt = new CompoundTag(); - connectedBlockEntity.getMainNode().saveToNBT(nbt); - tag.put("grid_node", nbt); - } - } - - @Override - public void loadCustomPersistedData(@NotNull CompoundTag tag) { - super.loadCustomPersistedData(tag); - if (GTCEu.Mods.isAE2Loaded() && gridNodeHost instanceof IGridConnectedBlockEntity connectedBlockEntity) { - connectedBlockEntity.getMainNode().loadFromNBT(tag.getCompound("grid_node")); - } - } - ////////////////////////////////////// // ********** Misc **********// ////////////////////////////////////// diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/IGridConnectedMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/IGridConnectedMachine.java index 8dcbca56391..3e24289ed48 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/IGridConnectedMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/IGridConnectedMachine.java @@ -1,57 +1,17 @@ package com.gregtechceu.gtceu.integration.ae2.machine.feature; -import com.gregtechceu.gtceu.api.machine.feature.IMachineFeature; -import com.gregtechceu.gtceu.config.ConfigHolder; - -import net.minecraft.core.Direction; - import appeng.api.networking.IGridNodeListener; -import appeng.api.util.AECableType; -import appeng.me.helpers.IGridConnectedBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IMachineFeature; /** * A machine that can connect to ME network. */ -public interface IGridConnectedMachine extends IMachineFeature, IGridConnectedBlockEntity { - - int ME_UPDATE_INTERVAL = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; +public interface IGridConnectedMachine extends IMachineFeature { /** - * @return return {@code true} if current machine connected to a valid ME network, {@code false} otherwise. + * Called when the block entities main grid nodes power or channel assignment state changes. Primarily used to send + * rendering updates to the client. */ - boolean isOnline(); - - void setOnline(boolean online); - - /** - * @return {@code true} if current machine should interact with ME network, {@code false} otherwise. - */ - default boolean shouldSyncME() { - return self().getOffsetTimer() % ME_UPDATE_INTERVAL == 0; - } - - default AECableType getCableConnectionType(Direction dir) { - return AECableType.SMART; - } - - /** - * Update me network connection status. - * - * @return the updated status. - */ - default boolean updateMEStatus() { - var proxy = getMainNode(); - setOnline(proxy.isOnline() && proxy.isPowered()); - return isOnline(); - } - - @Override - default void saveChanges() { - self().onChanged(); - } - - @Override default void onMainNodeStateChanged(IGridNodeListener.State reason) { - this.updateMEStatus(); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java deleted file mode 100644 index 50933b53c0f..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.machine.trait; - -import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; -import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; -import com.gregtechceu.gtceu.integration.ae2.utils.SerializableManagedGridNode; - -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.annotation.ReadOnlyManaged; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.TickTask; -import net.minecraft.server.level.ServerLevel; - -import appeng.api.networking.GridFlags; -import appeng.me.helpers.BlockEntityNodeListener; -import appeng.me.helpers.IGridConnectedBlockEntity; -import lombok.Getter; - -import java.util.EnumSet; - -/** - * A MachineTrait that is only used for hosting grid node and does not provide grid node capability. - * Because IGridConnectedMachine has already extended IInWorldGridNodeHost. - */ -public class GridNodeHolder extends MachineTrait { - - protected final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(GridNodeHolder.class); - - @Getter - @Persisted - @ReadOnlyManaged(onDirtyMethod = "onGridNodeDirty", - serializeMethod = "serializeGridNode", - deserializeMethod = "deserializeGridNode") - protected final SerializableManagedGridNode mainNode; - - public GridNodeHolder(IGridConnectedMachine machine) { - super(machine.self()); - this.mainNode = createManagedNode(); - } - - protected SerializableManagedGridNode createManagedNode() { - var node = (SerializableManagedGridNode) new SerializableManagedGridNode((IGridConnectedBlockEntity) machine, - BlockEntityNodeListener.INSTANCE) - .setFlags(GridFlags.REQUIRE_CHANNEL) - .setVisualRepresentation(machine.getDefinition().getItem()) - .setIdlePowerUsage(ConfigHolder.INSTANCE.compat.ae2.meHatchEnergyUsage) - .setInWorldNode(true) - .setExposedOnSides( - machine.hasFrontFacing() ? EnumSet.of(machine.getFrontFacing()) : - EnumSet.allOf(Direction.class)) - .setTagName("proxy"); - return node; - } - - protected void createMainNode() { - this.mainNode.create(machine.getLevel(), machine.getPos()); - } - - @Override - public void onMachineLoad() { - super.onMachineLoad(); - if (machine.getLevel() instanceof ServerLevel serverLevel) { - serverLevel.getServer().tell(new TickTask(0, this::createMainNode)); - } - } - - @Override - public void onMachineUnLoad() { - super.onMachineUnLoad(); - mainNode.destroy(); - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - @SuppressWarnings("unused") - public boolean onGridNodeDirty(SerializableManagedGridNode node) { - return node != null && node.isActive() && node.isOnline(); - } - - @SuppressWarnings("unused") - public CompoundTag serializeGridNode(SerializableManagedGridNode node) { - return node.serializeNBT(); - } - - @SuppressWarnings("unused") - public SerializableManagedGridNode deserializeGridNode(CompoundTag tag) { - this.mainNode.deserializeNBT(tag); - return this.mainNode; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java new file mode 100644 index 00000000000..9afd4fea0d4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java @@ -0,0 +1,88 @@ +package com.gregtechceu.gtceu.integration.ae2.machine.trait; + +import appeng.api.networking.GridHelper; +import appeng.api.networking.IGridNode; +import appeng.api.networking.IInWorldGridNodeHost; +import appeng.api.networking.IManagedGridNode; +import appeng.api.util.AECableType; +import appeng.me.InWorldGridNode; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.integration.ae2.utils.MachineNodeListener; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; +import org.jetbrains.annotations.Nullable; + +public class GridNodeHost extends MachineTrait implements IInWorldGridNodeHost { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(GridNodeHost.class); + + @Getter + private final IManagedGridNode mainNode; + + @Setter + private AECableType cableType = AECableType.SMART; + + public GridNodeHost(MetaMachine machine) { + super(machine); + this.mainNode = GridHelper.createManagedNode(machine, MachineNodeListener.INSTANCE) + .setInWorldNode(true) + .setVisualRepresentation(machine.getDefinition().getItem()); + } + + public @Nullable IGridNode getGridNode() { + return mainNode.getNode(); + } + + @Override + public @Nullable IGridNode getGridNode(Direction dir) { + var node = this.getMainNode().getNode(); + + // We use the node rather than getGridConnectableSides since the node is already using absolute sides + if (node instanceof InWorldGridNode inWorldGridNode && inWorldGridNode.isExposedOnSide(dir)) { + return node; + } + + return null; + } + + @Override + public AECableType getCableConnectionType(Direction dir) { + return cableType; + } + + @Override + public void onMachineLoad() { + // ensure the node is created after calling IManagedGridNode#loadFromNBT() + if (machine.getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell( + new TickTask(0, () -> mainNode.create(machine.getLevel(), machine.getPos())) + ); + } + } + + @Override + public void onMachineUnLoad() { + mainNode.destroy(); + } + + @Override + public void saveCustomPersistedData(CompoundTag tag, boolean forDrop) { + mainNode.saveToNBT(tag); + } + + @Override + public void loadCustomPersistedData(CompoundTag tag) { + mainNode.loadFromNBT(tag); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java deleted file mode 100644 index 41398dff529..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.machine.trait; - -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; - -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.core.Direction; - -import appeng.api.networking.GridHelper; -import appeng.api.networking.IManagedGridNode; -import appeng.api.util.AECableType; -import appeng.me.helpers.BlockEntityNodeListener; -import appeng.me.helpers.IGridConnectedBlockEntity; - -public class GridNodeHostTrait extends MachineTrait implements IGridConnectedBlockEntity { - - protected final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(GridNodeHostTrait.class); - private final IManagedGridNode proxy; - - public GridNodeHostTrait(MetaMachine machine) { - super(machine); - this.proxy = GridHelper.createManagedNode(this, BlockEntityNodeListener.INSTANCE) - .setInWorldNode(true) - .setVisualRepresentation(machine.getDefinition().getItem()); - } - - public void init() { - this.proxy.create(machine.getLevel(), machine.getPos()); - } - - @Override - public IManagedGridNode getMainNode() { - return proxy; - } - - @Override - public void saveChanges() { - machine.onChanged(); - } - - @Override - public AECableType getCableConnectionType(Direction dir) { - return AECableType.SMART; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java new file mode 100644 index 00000000000..57104e1a667 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import appeng.api.networking.IGridNode; +import appeng.api.networking.IGridNodeListener; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; + +public enum MachineNodeListener implements IGridNodeListener { + + INSTANCE; + + @Override + public void onSaveChanges(MetaMachine nodeOwner, IGridNode node) { + nodeOwner.onChanged(); + } + + @Override + public void onStateChanged(MetaMachine nodeOwner, IGridNode node, State state) { + if (nodeOwner instanceof IGridConnectedMachine machine) { + machine.onMainNodeStateChanged(state); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/SerializableManagedGridNode.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/SerializableManagedGridNode.java deleted file mode 100644 index a4a062247a5..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/SerializableManagedGridNode.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.utils; - -import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; - -import net.minecraft.nbt.CompoundTag; - -import appeng.api.networking.IGridNodeListener; -import appeng.me.ManagedGridNode; - -public class SerializableManagedGridNode extends ManagedGridNode implements ITagSerializable { - - public SerializableManagedGridNode(T nodeOwner, IGridNodeListener listener) { - super(nodeOwner, listener); - } - - @Override - public CompoundTag serializeNBT() { - CompoundTag tag = new CompoundTag(); - super.saveToNBT(tag); - return tag; - } - - @Override - public void deserializeNBT(CompoundTag tag) { - super.loadFromNBT(tag); - } -} From d5bf0be7966625d74cb5aa7ed5482a7e0f1af111 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Tue, 24 Mar 2026 18:00:38 +0800 Subject: [PATCH 02/21] refactor base classes for me part machine --- .../ae2/machine/MEBusPartMachine.java | 96 ++++++------------- .../ae2/machine/MEHatchPartMachine.java | 95 ++++++------------ 2 files changed, 60 insertions(+), 131 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java index 7bd09427d3b..69e05fe3c6c 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java @@ -1,96 +1,62 @@ package com.gregtechceu.gtceu.integration.ae2.machine; -import com.gregtechceu.gtceu.api.GTValues; +import appeng.api.networking.GridFlags; +import appeng.api.networking.IGridNode; +import appeng.api.networking.security.IActionHost; +import appeng.api.networking.security.IActionSource; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.common.machine.multiblock.part.ItemBusPartMachine; +import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHolder; - -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; - -import appeng.api.networking.*; -import appeng.api.networking.security.IActionSource; -import lombok.Getter; -import lombok.Setter; +import org.jetbrains.annotations.Nullable; import java.util.EnumSet; -import javax.annotation.ParametersAreNonnullByDefault; +public abstract class MEBusPartMachine extends ItemBusPartMachine implements IGridConnectedMachine, IActionHost { -@Getter -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public abstract class MEBusPartMachine extends ItemBusPartMachine implements IGridConnectedMachine { + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEBusPartMachine.class, + ItemBusPartMachine.MANAGED_FIELD_HOLDER + ); - protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(MEBusPartMachine.class, - ItemBusPartMachine.MANAGED_FIELD_HOLDER); + protected final GridNodeHost nodeHost; + protected final IActionSource actionSource = IActionSource.ofMachine(this); - @Persisted - protected final GridNodeHolder nodeHolder; - - @DescSynced - @Getter - @Setter - protected boolean isOnline; - - protected final IActionSource actionSource; - - public MEBusPartMachine(IMachineBlockEntity holder, IO io, Object... args) { - super(holder, GTValues.LuV, io, args); - this.nodeHolder = createNodeHolder(); - this.actionSource = IActionSource.ofMachine(nodeHolder.getMainNode()::getNode); + // bus LuV + // UHV FluidHatchPartMachine.INITIAL_TANK_CAPACITY_1X 16 + public MEBusPartMachine(IMachineBlockEntity holder, int tier, IO io, Object... args) { + super(holder, tier, io, args); + this.nodeHost = createNodeHost(); } - protected GridNodeHolder createNodeHolder() { - return new GridNodeHolder(this); + protected GridNodeHost createNodeHost() { + GridNodeHost host = new GridNodeHost(this); + host.getMainNode() + .setFlags(GridFlags.REQUIRE_CHANNEL) + .setIdlePowerUsage(ConfigHolder.INSTANCE.compat.ae2.meHatchEnergyUsage) + .setExposedOnSides( + hasFrontFacing() ? EnumSet.of(getFrontFacing()) : EnumSet.allOf(Direction.class) + ); + return host; } @Override - public IManagedGridNode getMainNode() { - return nodeHolder.getMainNode(); - } - - @Override - public void onMainNodeStateChanged(IGridNodeListener.State reason) { - IGridConnectedMachine.super.onMainNodeStateChanged(reason); - this.updateInventorySubscription(); - } - - @Override - protected void updateInventorySubscription() { - if (shouldSubscribe()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - } else if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } - } - - protected boolean shouldSubscribe() { - return isWorkingEnabled() && isOnline(); + public @Nullable IGridNode getActionableNode() { + return nodeHost.getMainNode().getNode(); } @Override public void onRotated(Direction oldFacing, Direction newFacing) { super.onRotated(oldFacing, newFacing); - getMainNode().setExposedOnSides(EnumSet.of(newFacing)); + nodeHost.getMainNode().setExposedOnSides(EnumSet.of(newFacing)); } @Override public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - - // By returning false here, we don't allow shift-clicking - // with a screwdriver to swap the IO. - @Override - public boolean swapIO() { - return false; - } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java index c7237baaf41..8dfeeb12d68 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java @@ -1,97 +1,60 @@ package com.gregtechceu.gtceu.integration.ae2.machine; -import com.gregtechceu.gtceu.api.GTValues; +import appeng.api.networking.GridFlags; +import appeng.api.networking.IGridNode; +import appeng.api.networking.security.IActionHost; +import appeng.api.networking.security.IActionSource; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.common.machine.multiblock.part.FluidHatchPartMachine; +import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHolder; - -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; - -import appeng.api.networking.*; -import appeng.api.networking.security.IActionSource; -import lombok.Getter; -import lombok.Setter; +import org.jetbrains.annotations.Nullable; import java.util.EnumSet; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public abstract class MEHatchPartMachine extends FluidHatchPartMachine implements IGridConnectedMachine { - - protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(MEHatchPartMachine.class, - FluidHatchPartMachine.MANAGED_FIELD_HOLDER); - - protected final static int CONFIG_SIZE = 16; - - @Persisted - protected final GridNodeHolder nodeHolder; - - @DescSynced - @Getter - @Setter - protected boolean isOnline; +public abstract class MEHatchPartMachine extends FluidHatchPartMachine implements IGridConnectedMachine, IActionHost { - protected final IActionSource actionSource; + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEHatchPartMachine.class, + FluidHatchPartMachine.MANAGED_FIELD_HOLDER + ); - public MEHatchPartMachine(IMachineBlockEntity holder, IO io, Object... args) { - super(holder, GTValues.UHV, io, FluidHatchPartMachine.INITIAL_TANK_CAPACITY_1X, CONFIG_SIZE, args); - this.nodeHolder = createNodeHolder(); - this.actionSource = IActionSource.ofMachine(nodeHolder.getMainNode()::getNode); - } + protected final GridNodeHost nodeHost; + protected final IActionSource actionSource = IActionSource.ofMachine(this); - protected GridNodeHolder createNodeHolder() { - return new GridNodeHolder(this); + public MEHatchPartMachine(IMachineBlockEntity holder, int tier, IO io, int initialCapacity, int slots, Object... args) { + super(holder, tier, io, initialCapacity, slots, args); + this.nodeHost = createNodeHost(); } - @Override - public IManagedGridNode getMainNode() { - return nodeHolder.getMainNode(); - } - - @Override - public void onMainNodeStateChanged(IGridNodeListener.State reason) { - IGridConnectedMachine.super.onMainNodeStateChanged(reason); - this.updateTankSubscription(); + protected GridNodeHost createNodeHost() { + GridNodeHost host = new GridNodeHost(this); + host.getMainNode() + .setFlags(GridFlags.REQUIRE_CHANNEL) + .setIdlePowerUsage(ConfigHolder.INSTANCE.compat.ae2.meHatchEnergyUsage) + .setExposedOnSides( + hasFrontFacing() ? EnumSet.of(getFrontFacing()) : EnumSet.allOf(Direction.class) + ); + return host; } @Override - protected void updateTankSubscription() { - if (shouldSubscribe()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - } else if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } - } - - protected boolean shouldSubscribe() { - return isWorkingEnabled() && isOnline(); + public @Nullable IGridNode getActionableNode() { + return nodeHost.getMainNode().getNode(); } @Override public void onRotated(Direction oldFacing, Direction newFacing) { super.onRotated(oldFacing, newFacing); - getMainNode().setExposedOnSides(EnumSet.of(newFacing)); + nodeHost.getMainNode().setExposedOnSides(EnumSet.of(newFacing)); } @Override public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - - // By returning false here, we don't allow shift-clicking - // with a screwdriver to swap the IO. - @Override - public boolean swapIO() { - return false; - } } From 57290b313d9ec4bd181e18b10f1f9317c8594205 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Tue, 24 Mar 2026 18:01:50 +0800 Subject: [PATCH 03/21] refactor ME input bus/hatch machines --- .../ae2/machine/MEInputBusPartMachine.java | 272 +++++++++--------- .../ae2/machine/MEInputHatchPartMachine.java | 250 ++++++++-------- .../ae2/utils/GenericStackHandler.java | 84 ++++++ 3 files changed, 334 insertions(+), 272 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java index d1c45b4f5c8..2c5c93ab86c 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java @@ -1,154 +1,172 @@ package com.gregtechceu.gtceu.integration.ae2.machine; +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.IManagedGridNode; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.GenericStack; +import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; -import com.gregtechceu.gtceu.api.machine.feature.IHasCircuitSlot; -import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.AEItemConfigWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; -import com.gregtechceu.gtceu.utils.GTMath; - -import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; -import com.lowdragmc.lowdraglib.gui.widget.Widget; -import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import com.lowdragmc.lowdraglib.utils.Position; - -import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import appeng.api.config.Actionable; -import appeng.api.stacks.GenericStack; -import appeng.api.storage.MEStorage; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class MEInputBusPartMachine extends MEBusPartMachine - implements IDataStickInteractable, IMachineLife, IHasCircuitSlot { +public class MEInputBusPartMachine extends MEBusPartMachine implements IDataStickInteractable { - protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(MEInputBusPartMachine.class, - MEBusPartMachine.MANAGED_FIELD_HOLDER); - protected final static int CONFIG_SIZE = 16; + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + MEInputBusPartMachine.class, + MEBusPartMachine.MANAGED_FIELD_HOLDER + ); - protected ExportOnlyAEItemList aeItemHandler; + @Persisted + protected final GenericStackHandler configStacks; + protected final int slots; - public MEInputBusPartMachine(IMachineBlockEntity holder, Object... args) { - super(holder, IO.IN, args); + public MEInputBusPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { + super(holder, tier, IO.IN, args); + this.configStacks = new GenericStackHandler(slots); + this.slots = slots; } - ///////////////////////////////// - // ***** Machine LifeCycle ****// - ///////////////////////////////// - @Override - public void onMachineRemoved() { - flushInventory(); + protected NotifiableItemStackHandler createInventory(Object... args) { + return new NotifiableItemStackHandler( + this, + getInventorySize(), + IO.IN, + IO.NONE, + slots -> new CustomItemStackHandler(slots) { + @Override + public int getSlotLimit(int slot) { + return Integer.MAX_VALUE; + } + + @Override + protected int getStackLimit(int slot, ItemStack stack) { + return Integer.MAX_VALUE; + } + } + ); } @Override - protected NotifiableItemStackHandler createInventory(Object... args) { - this.aeItemHandler = new ExportOnlyAEItemList(this, CONFIG_SIZE); - return this.aeItemHandler; + protected void updateInventorySubscription(Direction newFacing) { + IManagedGridNode node = nodeHost.getMainNode(); + if (isWorkingEnabled() && node.isActive()) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + return; + } + if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; + } } @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; + protected int getInventorySize() { + return slots; } - ///////////////////////////////// - // ********** Sync ME *********// - ///////////////////////////////// + @Override + public boolean swapIO() { + return false; + } @Override - public void autoIO() { - if (!this.isWorkingEnabled()) return; - if (!this.shouldSyncME()) return; + protected void autoIO() { + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; - if (this.updateMEStatus()) { - this.syncME(); - this.updateInventorySubscription(); - } - } + int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + if (getOffsetTimer() % updateInterval != 0) return; - protected void syncME() { - MEStorage networkInv = this.getMainNode().getGrid().getStorageService().getInventory(); - for (ExportOnlyAEItemSlot aeSlot : this.aeItemHandler.getInventory()) { - // Try to clear the wrong item - GenericStack exceedItem = aeSlot.exceedStack(); - if (exceedItem != null) { - long total = exceedItem.amount(); - long inserted = networkInv.insert(exceedItem.what(), exceedItem.amount(), Actionable.MODULATE, - this.actionSource); - if (inserted > 0) { - aeSlot.extractItem(0, GTMath.saturatedCast(inserted), false); - continue; - } else { - aeSlot.extractItem(0, GTMath.saturatedCast(total), false); - } - } - // Fill it - GenericStack reqItem = aeSlot.requestStack(); - if (reqItem != null) { - long extracted = networkInv.extract(reqItem.what(), reqItem.amount(), Actionable.MODULATE, - this.actionSource); - if (extracted != 0) { - aeSlot.addStack(new GenericStack(reqItem.what(), extracted)); - } + MEStorage networkInv = grid.getStorageService().getInventory(); + NotifiableItemStackHandler inventory = getInventory(); + + for (int i = 0; i < configStacks.getSlots(); i++) { + GenericStack configStack = configStacks.getStackInSlot(i); + if (configStack == null) continue; + AEItemKey configKey = (AEItemKey) configStack.what(); + long configAmount = configStack.amount(); + + // Ensure config amount does not exceed slot limit + if (configAmount > inventory.getSlotLimit(i)) { + throw new IllegalArgumentException("Config amount exceeds slot capacity!"); } - } - } - protected void flushInventory() { - var grid = getMainNode().getGrid(); - if (grid != null) { - for (var aeSlot : aeItemHandler.getInventory()) { - GenericStack stock = aeSlot.getStock(); - if (stock != null) { - grid.getStorageService().getInventory().insert(stock.what(), stock.amount(), Actionable.MODULATE, - actionSource); - } + // Ensure item in slot matches the config key + ItemStack stackInSlot = inventory.getStackInSlot(i); + if (!stackInSlot.isEmpty() && !configKey.matches(stackInSlot)) continue; + // Ensure slot has enough space + int actualAmount = stackInSlot.getCount(); + if (actualAmount >= configAmount) continue; + + ItemStack requestedStack = configKey.toStack(Math.toIntExact(configAmount - actualAmount)); + int inserted = requestedStack.getCount() - inventory.insertItem(i, requestedStack, true).getCount(); + if (inserted <= 0) continue; + + int extracted = Math.toIntExact( + networkInv.extract(configKey, inserted, Actionable.MODULATE, actionSource) + ); + if (extracted > 0) { + requestedStack.setCount(extracted); + inventory.insertItem(i, requestedStack, false); } } } - /////////////////////////////// - // ********** GUI ***********// - /////////////////////////////// - @Override - public Widget createUIWidget() { - WidgetGroup group = new WidgetGroup(new Position(0, 0)); - // ME Network status - group.addWidget(new LabelWidget(3, 0, () -> this.isOnline ? - "gtceu.gui.me_network.online" : - "gtceu.gui.me_network.offline")); - - // Config slots - group.addWidget(new AEItemConfigWidget(3, 10, this.aeItemHandler)); + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateInventorySubscription(); + } - return group; + @Override + public void onMachineRemoved() { + nodeHost.getMainNode().ifPresent(grid -> { + var networkInv = grid.getStorageService().getInventory(); + NotifiableItemStackHandler inventory = getInventory(); + for (int i = 0; i < inventory.getSlots(); i++) { + ItemStack stack = inventory.getStackInSlot(i); + if (stack.isEmpty()) continue; + networkInv.insert(AEItemKey.of(stack), stack.getCount(), Actionable.MODULATE, actionSource); + } + }); + super.onMachineRemoved(); } - //////////////////////////////// - // ******* Interaction *******// - //////////////////////////////// +// @Override +// public Widget createUIWidget() { +// WidgetGroup group = new WidgetGroup(new Position(0, 0)); +// // ME Network status +// group.addWidget(new LabelWidget(3, 0, () -> this.isOnline ? +// "gtceu.gui.me_network.online" : +// "gtceu.gui.me_network.offline")); +// +// // Config slots +// group.addWidget(new AEItemConfigWidget(3, 10, this.aeItemHandler)); +// +// return group; +// } @Override public final InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { if (!isRemote()) { CompoundTag tag = new CompoundTag(); - tag.put("MEInputBus", writeConfigToTag()); + tag.put("MEInputBus", writeConfig()); dataStick.setTag(tag); dataStick.setHoverName(Component.translatable("gtceu.machine.me.item_import.data_stick.name")); player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); @@ -162,50 +180,27 @@ public final InteractionResult onDataStickUse(Player player, ItemStack dataStick if (tag == null || !tag.contains("MEInputBus")) { return InteractionResult.PASS; } - if (!isRemote()) { - readConfigFromTag(tag.getCompound("MEInputBus")); - this.updateInventorySubscription(); + readConfig(tag.getCompound("MEInputBus")); player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); } return InteractionResult.sidedSuccess(isRemote()); } - //////////////////////////////// - // ****** Configuration ******// - //////////////////////////////// - - protected CompoundTag writeConfigToTag() { + protected CompoundTag writeConfig() { CompoundTag tag = new CompoundTag(); - CompoundTag configStacks = new CompoundTag(); - tag.put("ConfigStacks", configStacks); - for (int i = 0; i < CONFIG_SIZE; i++) { - var slot = this.aeItemHandler.getInventory()[i]; - GenericStack config = slot.getConfig(); - if (config == null) { - continue; - } - CompoundTag stackTag = GenericStack.writeTag(config); - configStacks.put(Integer.toString(i), stackTag); - } - tag.putByte("GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0))); + tag.put("ConfigStacks", configStacks.serializeNBT()); + tag.putByte( + "GhostCircuit", + (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) + ); tag.putBoolean("DistinctBuses", isDistinct()); return tag; } - protected void readConfigFromTag(CompoundTag tag) { + protected void readConfig(CompoundTag tag) { if (tag.contains("ConfigStacks")) { - CompoundTag configStacks = tag.getCompound("ConfigStacks"); - for (int i = 0; i < CONFIG_SIZE; i++) { - String key = Integer.toString(i); - if (configStacks.contains(key)) { - CompoundTag configTag = configStacks.getCompound(key); - this.aeItemHandler.getInventory()[i].setConfig(GenericStack.readTag(configTag)); - } else { - this.aeItemHandler.getInventory()[i].setConfig(null); - } - } + configStacks.deserializeNBT(tag.getCompound("ConfigStacks")); } if (tag.contains("GhostCircuit")) { circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); @@ -214,4 +209,9 @@ protected void readConfigFromTag(CompoundTag tag) { setDistinct(tag.getBoolean("DistinctBuses")); } } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java index 14b821bf6dc..6a81ec37849 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java @@ -1,155 +1,151 @@ package com.gregtechceu.gtceu.integration.ae2.machine; +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.IManagedGridNode; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.GenericStack; +import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; -import com.gregtechceu.gtceu.api.machine.feature.IHasCircuitSlot; -import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.AEFluidConfigWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; -import com.gregtechceu.gtceu.utils.GTMath; - -import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; -import com.lowdragmc.lowdraglib.gui.widget.Widget; -import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import com.lowdragmc.lowdraglib.utils.Position; - -import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.templates.FluidTank; -import appeng.api.config.Actionable; -import appeng.api.stacks.GenericStack; -import appeng.api.storage.MEStorage; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class MEInputHatchPartMachine extends MEHatchPartMachine - implements IDataStickInteractable, IMachineLife, IHasCircuitSlot { +public class MEInputHatchPartMachine extends MEHatchPartMachine implements IDataStickInteractable { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - MEInputHatchPartMachine.class, MEHatchPartMachine.MANAGED_FIELD_HOLDER); + MEInputHatchPartMachine.class, + MEHatchPartMachine.MANAGED_FIELD_HOLDER + ); - protected ExportOnlyAEFluidList aeFluidHandler; + @Persisted + protected final GenericStackHandler configStacks; - public MEInputHatchPartMachine(IMachineBlockEntity holder, Object... args) { - super(holder, IO.IN, args); - } - - ///////////////////////////////// - // ***** Machine LifeCycle ****// - ///////////////////////////////// - - @Override - public void onMachineRemoved() { - flushInventory(); + public MEInputHatchPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { + super(holder, tier, IO.IN, -1, slots, args); + this.configStacks = new GenericStackHandler(slots); } @Override protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object... args) { - this.aeFluidHandler = new ExportOnlyAEFluidList(this, slots); - return aeFluidHandler; + return new NotifiableFluidTank(this, slots, Integer.MAX_VALUE, IO.IN, IO.NONE); } @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; + protected void updateTankSubscription(Direction newFacing) { + IManagedGridNode node = nodeHost.getMainNode(); + if (isWorkingEnabled() && node.isActive()) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + } else if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; + } } - ///////////////////////////////// - // ********** Sync ME *********// - ///////////////////////////////// - @Override protected void autoIO() { - if (!this.isWorkingEnabled()) return; - if (!this.shouldSyncME()) return; + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; - if (this.updateMEStatus()) { - this.syncME(); - this.updateTankSubscription(); - } - } + int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + if (self().getOffsetTimer() % updateInterval != 0) return; - protected void syncME() { - MEStorage networkInv = this.getMainNode().getGrid().getStorageService().getInventory(); - for (ExportOnlyAEFluidSlot aeTank : this.aeFluidHandler.getInventory()) { - // Try to clear the wrong fluid - GenericStack exceedFluid = aeTank.exceedStack(); - if (exceedFluid != null) { - int total = GTMath.saturatedCast(exceedFluid.amount()); - int inserted = GTMath - .saturatedCast(networkInv.insert(exceedFluid.what(), exceedFluid.amount(), Actionable.MODULATE, - this.actionSource)); - if (inserted > 0) { - aeTank.drain(inserted, IFluidHandler.FluidAction.EXECUTE); - continue; - } else { - aeTank.drain(total, IFluidHandler.FluidAction.EXECUTE); - } - } - // Fill it - GenericStack reqFluid = aeTank.requestStack(); - if (reqFluid != null) { - long extracted = networkInv.extract(reqFluid.what(), reqFluid.amount(), Actionable.MODULATE, - this.actionSource); - if (extracted > 0) { - aeTank.addStack(new GenericStack(reqFluid.what(), extracted)); - } + MEStorage networkInv = grid.getStorageService().getInventory(); + FluidTank[] tanks = tank.getStorages(); + + for (int i = 0; i < configStacks.getSlots(); i++) { + GenericStack configStack = configStacks.getStackInSlot(i); + if (configStack == null) continue; + AEFluidKey configKey = (AEFluidKey) configStack.what(); + long configAmount = configStack.amount(); + + FluidTank tank = tanks[i]; + // Ensure config amount does not exceed tank capacity + if (configAmount > tank.getCapacity()) { + throw new IllegalArgumentException("Config amount exceeds tank capacity!"); } - } - } - protected void flushInventory() { - var grid = getMainNode().getGrid(); - if (grid != null) { - for (var aeSlot : aeFluidHandler.getInventory()) { - GenericStack stock = aeSlot.getStock(); - if (stock != null) { - grid.getStorageService().getInventory().insert(stock.what(), stock.amount(), Actionable.MODULATE, - actionSource); - } + // Ensure fluid in tank matches the config key + if (!tank.isEmpty() && !configKey.matches(tank.getFluid())) continue; + // Ensure tank has enough space + int actualAmount = tank.getFluidAmount(); + if (actualAmount >= configAmount) continue; + + FluidStack requestedStack = configKey.toStack(Math.toIntExact(configAmount - actualAmount)); + int inserted = tank.fill( + requestedStack, + IFluidHandler.FluidAction.SIMULATE + ); + if (inserted <= 0) continue; + + int extracted = Math.toIntExact( + networkInv.extract(configKey, inserted, Actionable.MODULATE, actionSource) + ); + if (extracted > 0) { + requestedStack.setAmount(extracted); + tank.fill(requestedStack, IFluidHandler.FluidAction.EXECUTE); } } } - /////////////////////////////// - // ********** GUI ***********// - /////////////////////////////// - @Override - public Widget createUIWidget() { - WidgetGroup group = new WidgetGroup(new Position(0, 0)); - // ME Network status - group.addWidget(new LabelWidget(3, 0, () -> this.isOnline ? - "gtceu.gui.me_network.online" : - "gtceu.gui.me_network.offline")); + public boolean swapIO() { + return false; + } - // Config slots - group.addWidget(new AEFluidConfigWidget(3, 10, this.aeFluidHandler)); + @Override + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateTankSubscription(); + } - return group; + @Override + public void onMachineRemoved() { + nodeHost.getMainNode().ifPresent(grid -> { + for (var storage : tank.getStorages()) { + FluidStack stack = storage.getFluid(); + if (stack.isEmpty()) continue; + grid.getStorageService().getInventory().insert( + AEFluidKey.of(stack), stack.getAmount(), Actionable.MODULATE, actionSource + ); + } + }); + super.onMachineRemoved(); } - //////////////////////////////// - // ******* Interaction *******// - //////////////////////////////// +// @Override +// public Widget createUIWidget() { +// WidgetGroup group = new WidgetGroup(new Position(0, 0)); +// // ME Network status +// group.addWidget(new LabelWidget(3, 0, () -> this.isOnline ? +// "gtceu.gui.me_network.online" : +// "gtceu.gui.me_network.offline")); +// +// // Config slots +// group.addWidget(new AEFluidConfigWidget(3, 10, this.aeFluidHandler)); +// return group; +// } @Override public final InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { if (!isRemote()) { CompoundTag tag = new CompoundTag(); - tag.put("MEInputHatch", writeConfigToTag()); + tag.put("MEInputHatch", writeConfig()); dataStick.setTag(tag); dataStick.setHoverName(Component.translatable("gtceu.machine.me.fluid_import.data_stick.name")); player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); @@ -163,52 +159,34 @@ public final InteractionResult onDataStickUse(Player player, ItemStack dataStick if (tag == null || !tag.contains("MEInputHatch")) { return InteractionResult.PASS; } - if (!isRemote()) { - readConfigFromTag(tag.getCompound("MEInputHatch")); - this.updateTankSubscription(); + readConfig(tag.getCompound("MEInputHatch")); player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); } return InteractionResult.sidedSuccess(isRemote()); } - //////////////////////////////// - // ****** Configuration ******// - //////////////////////////////// - - protected CompoundTag writeConfigToTag() { + protected CompoundTag writeConfig() { CompoundTag tag = new CompoundTag(); - CompoundTag configStacks = new CompoundTag(); - tag.put("ConfigStacks", configStacks); - for (int i = 0; i < CONFIG_SIZE; i++) { - var slot = this.aeFluidHandler.getInventory()[i]; - GenericStack config = slot.getConfig(); - if (config == null) { - continue; - } - CompoundTag stackTag = GenericStack.writeTag(config); - configStacks.put(Integer.toString(i), stackTag); - } - tag.putByte("GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0))); + tag.put("ConfigStacks", configStacks.serializeNBT()); + tag.putByte( + "GhostCircuit", + (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) + ); return tag; } - protected void readConfigFromTag(CompoundTag tag) { + protected void readConfig(CompoundTag tag) { if (tag.contains("ConfigStacks")) { - CompoundTag configStacks = tag.getCompound("ConfigStacks"); - for (int i = 0; i < CONFIG_SIZE; i++) { - String key = Integer.toString(i); - if (configStacks.contains(key)) { - CompoundTag configTag = configStacks.getCompound(key); - this.aeFluidHandler.getInventory()[i].setConfig(GenericStack.readTag(configTag)); - } else { - this.aeFluidHandler.getInventory()[i].setConfig(null); - } - } + configStacks.deserializeNBT(tag.getCompound("ConfigStacks")); } if (tag.contains("GhostCircuit")) { circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); } } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java new file mode 100644 index 00000000000..0e7e487a634 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java @@ -0,0 +1,84 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import appeng.api.stacks.GenericStack; +import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import org.jetbrains.annotations.Nullable; + +/** + * Multi-slot GenericStack storage handler, similar to ItemStackHandler. + */ +public class GenericStackHandler implements ITagSerializable, IContentChangeAware { + + protected GenericStack[] stacks; + + @Getter + @Setter + private Runnable onContentsChanged = () -> { + }; + + public GenericStackHandler(int size) { + this.stacks = new GenericStack[size]; + } + + public int getSlots() { + return stacks.length; + } + + public @Nullable GenericStack getStackInSlot(int slot) { + validateSlotIndex(slot); + return stacks[slot]; + } + + public void setStackInSlot(int slot, @Nullable GenericStack stack) { + validateSlotIndex(slot); + stacks[slot] = stack; + onContentsChanged(slot); + } + + protected void validateSlotIndex(int slot) { + if (slot < 0 || slot >= stacks.length) + throw new RuntimeException("Slot " + slot + " not in valid range - [0," + stacks.length + ")"); + } + + protected void onContentsChanged(int slot) { + onContentsChanged.run(); + } + + @Override + public CompoundTag serializeNBT() { + var nbt = new CompoundTag(); + var stacksTag = new ListTag(); + for (int i = 0; i < stacks.length; i++) { + var stack = stacks[i]; + if (stack != null) { + var stackTag = GenericStack.writeTag(stack); + stackTag.putInt("Slot", i); + stacksTag.add(stackTag); + } + } + nbt.put("Stacks", stacksTag); + nbt.putInt("Size", stacks.length); + return nbt; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + int size = nbt.contains("Size", Tag.TAG_INT) ? nbt.getInt("Size") : stacks.length; + this.stacks = new GenericStack[size]; + + var stacksTag = nbt.getList("Stacks", Tag.TAG_COMPOUND); + for (int i = 0; i < stacksTag.size(); i++) { + var stackTag = stacksTag.getCompound(i); + int slot = stackTag.getInt("Slot"); + if (slot >= 0 && slot < stacks.length) { + stacks[slot] = GenericStack.readTag(stackTag); + } + } + } +} From 25d229faea83dd19e71a10c35d53d9bbb3a6c524 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Tue, 24 Mar 2026 18:10:38 +0800 Subject: [PATCH 04/21] remove export-only AE2 slot classes --- .../ae2/slot/ExportOnlyAEFluidList.java | 144 -------------- .../ae2/slot/ExportOnlyAEFluidSlot.java | 151 --------------- .../ae2/slot/ExportOnlyAEItemList.java | 177 ------------------ .../ae2/slot/ExportOnlyAEItemSlot.java | 115 ------------ .../ae2/slot/ExportOnlyAESlot.java | 113 ----------- 5 files changed, 700 deletions(-) delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidList.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidSlot.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemList.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemSlot.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAESlot.java diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidList.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidList.java deleted file mode 100644 index a87d46b4269..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidList.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; -import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; - -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraftforge.fluids.FluidStack; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Supplier; - -public class ExportOnlyAEFluidList extends NotifiableFluidTank implements IConfigurableSlotList { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - ExportOnlyAEFluidList.class, NotifiableFluidTank.MANAGED_FIELD_HOLDER); - - @Getter - @Persisted - protected ExportOnlyAEFluidSlot[] inventory; - - public ExportOnlyAEFluidList(MetaMachine machine, int slots) { - this(machine, slots, ExportOnlyAEFluidSlot::new); - } - - public ExportOnlyAEFluidList(MetaMachine machine, int slots, Supplier slotFactory) { - super(machine, slots, 0, IO.IN); - this.inventory = new ExportOnlyAEFluidSlot[slots]; - for (int i = 0; i < slots; i++) { - this.inventory[i] = slotFactory.get(); - this.inventory[i].setOnContentsChanged(this::onContentsChanged); - this.storages[i] = new FluidStorageDelegate(inventory[i]); - } - } - - @Override - public int fill(FluidStack resource, FluidAction action) { - return 0; - } - - @Override - public boolean supportsFill(int tank) { - return false; - } - - @Override - public FluidStack drainInternal(int maxDrain, FluidAction action) { - if (maxDrain == 0) { - return FluidStack.EMPTY; - } - FluidStack totalDrained = null; - for (var tank : inventory) { - if (totalDrained == null || totalDrained.isEmpty()) { - totalDrained = tank.drain(maxDrain, action); - if (totalDrained.isEmpty()) { - totalDrained = null; - } else { - maxDrain -= totalDrained.getAmount(); - } - } else { - FluidStack copy = totalDrained.copy(); - copy.setAmount(maxDrain); - FluidStack drain = tank.drain(copy, action); - totalDrained.grow(drain.getAmount()); - maxDrain -= drain.getAmount(); - } - if (maxDrain <= 0) break; - } - return totalDrained == null ? FluidStack.EMPTY : totalDrained; - } - - @Override - public IConfigurableSlot getConfigurableSlot(int index) { - return inventory[index]; - } - - @Override - public int getConfigurableSlots() { - return inventory.length; - } - - public boolean isAutoPull() { - return false; - } - - public boolean isStocking() { - return false; - } - - public boolean ownsSlot(ExportOnlyAEFluidSlot testSlot) { - for (var tank : inventory) { - if (tank == testSlot) { - return true; - } - } - return false; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - private static class FluidStorageDelegate extends CustomFluidTank { - - private final ExportOnlyAEFluidSlot fluid; - - public FluidStorageDelegate(ExportOnlyAEFluidSlot fluid) { - super(0); - this.fluid = fluid; - } - - @Override - @NotNull - public FluidStack getFluid() { - return this.fluid.getFluid(); - } - - @Override - public @NotNull FluidStack drain(int maxDrain, FluidAction action) { - return fluid.drain(maxDrain, action); - } - - @Override - public @NotNull FluidStack drain(FluidStack resource, FluidAction action) { - return fluid.drain(resource, action); - } - - @Override - public int fill(FluidStack resource, FluidAction action) { - return 0; - } - - @Override - public boolean supportsFill(int tank) { - return false; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidSlot.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidSlot.java deleted file mode 100644 index da1f5403259..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEFluidSlot.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.utils.GTMath; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.IFluidTank; - -import appeng.api.stacks.AEFluidKey; -import appeng.api.stacks.GenericStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class ExportOnlyAEFluidSlot extends ExportOnlyAESlot implements IFluidHandlerModifiable, IFluidTank { - - public ExportOnlyAEFluidSlot() { - super(); - } - - public ExportOnlyAEFluidSlot(@Nullable GenericStack config, @Nullable GenericStack stock) { - super(config, stock); - } - - @Override - public void addStack(GenericStack stack) { - if (this.stock == null) { - this.stock = stack; - } else { - this.stock = GenericStack.sum(this.stock, stack); - } - onContentsChanged(); - } - - @Override - public void setStock(@Nullable GenericStack stack) { - if (this.stock == null && stack == null) { - return; - } else if (stack == null) { - this.stock = null; - } else { - if (stack.equals(stock)) return; - this.stock = stack; - } - onContentsChanged(); - } - - @Override - public FluidStack getFluid() { - if (this.stock != null && this.stock.what() instanceof AEFluidKey fluidKey) { - return fluidKey.toStack(GTMath.saturatedCast(this.stock.amount())); - } - return FluidStack.EMPTY; - } - - @Override - public boolean isFluidValid(FluidStack stack) { - return false; - } - - @Override - public int getFluidAmount() { - return this.stock != null ? GTMath.saturatedCast(this.stock.amount()) : 0; - } - - @Override - public int getCapacity() { - // Its capacity is always 0. - return 0; - } - - @Override - public int getTanks() { - return 0; - } - - @Override - public FluidStack getFluidInTank(int tank) { - return getFluid(); - } - - @Override - public void setFluidInTank(int tank, FluidStack stack) {} - - @Override - public int getTankCapacity(int tank) { - return 0; - } - - @Override - public boolean isFluidValid(int tank, @NotNull FluidStack stack) { - return false; - } - - @Override - public int fill(FluidStack resource, FluidAction action) { - return 0; - } - - @Override - public boolean supportsFill(int tank) { - return false; - } - - @Override - public FluidStack drain(FluidStack resource, FluidAction action) { - if (this.getFluid().isFluidEqual(resource)) { - return this.drain(resource.getAmount(), action); - } - return FluidStack.EMPTY; - } - - @Override - public FluidStack drain(int maxDrain, FluidAction action) { - if (this.stock == null || !(this.stock.what() instanceof AEFluidKey fluidKey)) { - return FluidStack.EMPTY; - } - int drained = (int) Math.min(this.stock.amount(), maxDrain); - FluidStack result = fluidKey.toStack(drained); - if (action.execute()) { - this.stock = new GenericStack(this.stock.what(), this.stock.amount() - drained); - if (this.stock.amount() == 0) { - this.stock = null; - } - onContentsChanged(); - } - return result; - } - - @Override - public boolean supportsDrain(int tank) { - return tank == 0; - } - - public void onContentsChanged() { - if (onContentsChanged != null) { - onContentsChanged.run(); - } - } - - @Override - public ExportOnlyAEFluidSlot copy() { - return new ExportOnlyAEFluidSlot( - this.config == null ? null : ExportOnlyAESlot.copy(this.config), - this.stock == null ? null : ExportOnlyAESlot.copy(this.stock)); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemList.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemList.java deleted file mode 100644 index a757ca42bfa..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemList.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; - -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.function.Supplier; - -public class ExportOnlyAEItemList extends NotifiableItemStackHandler implements IConfigurableSlotList { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(ExportOnlyAEItemList.class, - NotifiableItemStackHandler.MANAGED_FIELD_HOLDER); - - @Persisted - @Getter - protected ExportOnlyAEItemSlot[] inventory; - - private CustomItemStackHandler itemHandler; - - public ExportOnlyAEItemList(MetaMachine holder, int slots) { - this(holder, slots, ExportOnlyAEItemSlot::new); - } - - public ExportOnlyAEItemList(MetaMachine holder, int slots, Supplier slotFactory) { - super(holder, 0, IO.IN, IO.NONE); - this.inventory = new ExportOnlyAEItemSlot[slots]; - for (int i = 0; i < slots; i++) { - this.inventory[i] = slotFactory.get(); - } - for (ExportOnlyAEItemSlot slot : this.inventory) { - slot.setOnContentsChanged(this::onContentsChanged); - } - } - - public CustomItemStackHandler getHandler() { - if (this.itemHandler == null) { - this.itemHandler = new ItemStackHandlerDelegate(inventory); - } - return itemHandler; - } - - @Override - public int getSlotLimit(int slot) { - return Integer.MAX_VALUE; - } - - @Override - public int getSlots() { - return inventory.length; - } - - @Override - public void setStackInSlot(int slot, @NotNull ItemStack stack) { - // NO-OP - } - - @NotNull - @Override - public ItemStack getStackInSlot(int slot) { - if (slot >= 0 && slot < inventory.length) { - return this.inventory[slot].getStackInSlot(0); - } - return ItemStack.EMPTY; - } - - @NotNull - @Override - public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { - return stack; - } - - @NotNull - @Override - public ItemStack extractItemInternal(int slot, int amount, boolean simulate) { - if (slot >= 0 && slot < inventory.length) { - return this.inventory[slot].extractItem(0, amount, simulate); - } - return ItemStack.EMPTY; - } - - @Override - public List handleRecipeInner(IO io, GTRecipe recipe, List left, - boolean simulate) { - return NotifiableItemStackHandler.handleRecipe(io, recipe, left, simulate, this.handlerIO, getHandler()); - } - - @Override - public IConfigurableSlot getConfigurableSlot(int index) { - return inventory[index]; - } - - @Override - public int getConfigurableSlots() { - return inventory.length; - } - - public boolean isAutoPull() { - return false; - } - - public boolean isStocking() { - return false; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - private static class ItemStackHandlerDelegate extends CustomItemStackHandler { - - private final ExportOnlyAEItemSlot[] inventory; - - public ItemStackHandlerDelegate(ExportOnlyAEItemSlot[] inventory) { - super(); - this.inventory = inventory; - } - - @Override - public int getSlots() { - return inventory.length; - } - - @Override - public ItemStack getStackInSlot(int slot) { - return inventory[slot].getStackInSlot(0); - } - - @Override - public void setStackInSlot(int slot, ItemStack stack) { - // NO-OP - } - - @Override - @NotNull - public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { - return stack; - } - - @Override - @NotNull - public ItemStack extractItem(int slot, int amount, boolean simulate) { - if (amount == 0) return ItemStack.EMPTY; - validateSlotIndex(slot); - return inventory[slot].extractItem(0, amount, simulate); - } - - @Override - protected void validateSlotIndex(int slot) { - if (slot < 0 || slot >= getSlots()) - throw new RuntimeException( - "Slot " + slot + " not in valid range - [0," + getSlots() + ")"); - } - - @Override - public int getSlotLimit(int slot) { - return Integer.MAX_VALUE; - } - - @Override - public boolean isItemValid(int slot, ItemStack stack) { - return false; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemSlot.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemSlot.java deleted file mode 100644 index f0ce2d0bed5..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAEItemSlot.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import com.gregtechceu.gtceu.utils.GTMath; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.IItemHandlerModifiable; - -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.GenericStack; -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class ExportOnlyAEItemSlot extends ExportOnlyAESlot implements IItemHandlerModifiable { - - public ExportOnlyAEItemSlot() { - super(); - } - - public ExportOnlyAEItemSlot(@Nullable GenericStack config, @Nullable GenericStack stock) { - super(config, stock); - } - - @Override - public void addStack(GenericStack stack) { - if (this.stock == null) { - this.stock = stack; - } else { - this.stock = GenericStack.sum(this.stock, stack); - } - onContentsChanged(); - } - - @Override - public void setStock(@Nullable GenericStack stack) { - if (this.stock == null && stack == null) { - return; - } else if (stack == null) { - this.stock = null; - } else { - if (stack.equals(stock)) return; - this.stock = stack; - } - onContentsChanged(); - } - - @Override - public int getSlots() { - return 1; - } - - @Override - public void setStackInSlot(int slot, ItemStack stack) { - // NO-OP - } - - @Override - public ItemStack getStackInSlot(int slot) { - if (slot == 0 && this.stock != null) { - return this.stock.what() instanceof AEItemKey itemKey ? - itemKey.toStack(GTMath.saturatedCast(this.stock.amount())) : - ItemStack.EMPTY; - } - return ItemStack.EMPTY; - } - - @Override - public int getSlotLimit(int slot) { - return Integer.MAX_VALUE; - } - - @Override - public boolean isItemValid(int slot, ItemStack stack) { - return false; - } - - @Override - public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { - return stack; - } - - @Override - public ItemStack extractItem(int slot, int amount, boolean simulate) { - if (slot == 0 && this.stock != null) { - int extracted = (int) Math.min(this.stock.amount(), amount); - if (!(this.stock.what() instanceof AEItemKey itemKey)) return ItemStack.EMPTY; - ItemStack result = itemKey.toStack(extracted); - if (!simulate) { - this.stock = ExportOnlyAESlot.copy(this.stock, this.stock.amount() - extracted); - if (this.stock.amount() == 0) { - this.stock = null; - } - } - onContentsChanged(); - return result; - } - return ItemStack.EMPTY; - } - - public void onContentsChanged() { - if (onContentsChanged != null) { - onContentsChanged.run(); - } - } - - @Override - public ExportOnlyAEItemSlot copy() { - return new ExportOnlyAEItemSlot( - this.config == null ? null : copy(this.config), - this.stock == null ? null : copy(this.stock)); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAESlot.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAESlot.java deleted file mode 100644 index b98fe200f5a..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/ExportOnlyAESlot.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; -import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; - -import net.minecraft.nbt.CompoundTag; - -import appeng.api.stacks.GenericStack; -import lombok.Getter; -import lombok.Setter; -import org.jetbrains.annotations.Nullable; - -/** - * An export only slot to hold {@link appeng.api.stacks.GenericStack} - */ -public abstract class ExportOnlyAESlot implements IConfigurableSlot, ITagSerializable, - IContentChangeAware { - - protected final static String CONFIG_TAG = "config"; - protected final static String STOCK_TAG = "stock"; - - @Getter - @Setter - protected Runnable onContentsChanged = () -> {}; - - @Getter - @Setter - @Nullable - protected GenericStack config; - @Getter - @Setter - @Nullable - protected GenericStack stock; - - public ExportOnlyAESlot(@Nullable GenericStack config, @Nullable GenericStack stock) { - this.config = config; - this.stock = stock; - } - - public ExportOnlyAESlot() { - this(null, null); - } - - @Nullable - public GenericStack requestStack() { - if (this.stock != null && this.stock.amount() <= 0) { - this.stock = null; - } - if (this.config == null || (this.stock != null && !this.config.what().matches(this.stock))) { - return null; - } - if (this.stock == null) { - return copy(this.config); - } - if (this.stock.amount() <= this.config.amount()) { - return copy(this.config, this.config.amount() - this.stock.amount()); - } - return null; - } - - @Nullable - public GenericStack exceedStack() { - if (this.stock != null && this.stock.amount() <= 0) { - this.stock = null; - } - if (this.config == null && this.stock != null) { - return copy(this.stock); - } - if (this.config != null && this.stock != null) { - if (this.config.what().matches(this.stock) && this.config.amount() < this.stock.amount()) { - return copy(this.stock, this.stock.amount() - this.config.amount()); - } - if (!this.config.what().matches(this.stock)) { - return copy(this.stock); - } - } - return null; - } - - protected abstract void addStack(GenericStack stack); - - @Override - public CompoundTag serializeNBT() { - CompoundTag tag = new CompoundTag(); - if (this.config != null) { - CompoundTag configTag = GenericStack.writeTag(this.config); - tag.put(CONFIG_TAG, configTag); - } - if (this.stock != null) { - CompoundTag stockTag = GenericStack.writeTag(this.stock); - tag.put(STOCK_TAG, stockTag); - } - return tag; - } - - @Override - public void deserializeNBT(CompoundTag tag) { - if (tag.contains(CONFIG_TAG)) { - this.config = GenericStack.readTag(tag.getCompound(CONFIG_TAG)); - } - if (tag.contains(STOCK_TAG)) { - this.stock = GenericStack.readTag(tag.getCompound(STOCK_TAG)); - } - } - - public static GenericStack copy(GenericStack stack) { - return new GenericStack(stack.what(), stack.amount()); - } - - public static GenericStack copy(GenericStack stack, long amount) { - return new GenericStack(stack.what(), amount); - } -} From 1bcb42f8ac2d8447384680b6c80a866ec6e74229 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Tue, 24 Mar 2026 18:13:54 +0800 Subject: [PATCH 05/21] remove AE2 multiblock part interfaces --- .../feature/multiblock/IAutoPullPart.java | 32 --------- .../feature/multiblock/IMEStockingPart.java | 67 ------------------- .../integration/ae2/machine/package-info.java | 7 ++ .../ae2/machine/trait/package-info.java | 7 ++ .../ae2/slot/IConfigurableSlot.java | 19 ------ .../ae2/slot/IConfigurableSlotList.java | 30 --------- .../gtceu/integration/ae2/utils/AEUtil.java | 58 ---------------- .../integration/ae2/utils/package-info.java | 7 ++ 8 files changed, 21 insertions(+), 206 deletions(-) delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IAutoPullPart.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IMEStockingPart.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/package-info.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/package-info.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlot.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlotList.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/package-info.java diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IAutoPullPart.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IAutoPullPart.java deleted file mode 100644 index cc6db1d8336..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IAutoPullPart.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.machine.feature.multiblock; - -import com.gregtechceu.gtceu.api.gui.GuiTextures; -import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; -import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; - -import net.minecraft.network.chat.Component; - -import appeng.api.stacks.GenericStack; - -import java.util.List; -import java.util.function.Predicate; - -public interface IAutoPullPart extends IMultiPart { - - boolean isAutoPull(); - - void setAutoPull(boolean autoPull); - - void setAutoPullTest(Predicate test); - - @Override - default void attachConfigurators(ConfiguratorPanel configuratorPanel) { - configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( - GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), - GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), - this::isAutoPull, - (clickData, pressed) -> setAutoPull(pressed)) - .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IMEStockingPart.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IMEStockingPart.java deleted file mode 100644 index 75515fcaaab..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/feature/multiblock/IMEStockingPart.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.machine.feature.multiblock; - -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlotList; - -import net.minecraft.server.TickTask; -import net.minecraft.server.level.ServerLevel; - -import appeng.api.stacks.GenericStack; -import org.jetbrains.annotations.Nullable; - -public interface IMEStockingPart extends IAutoPullPart { - - @Override - default void addedToController(IMultiController controller) { - // ensure that no other stocking bus on this multiblock is configured to hold the same item. - // that we have in our own bus. - setAutoPullTest(stack -> !this.testConfiguredInOtherPart(stack)); - // also ensure that our current config is valid given other inputs - if (self().getLevel() instanceof ServerLevel serverLevel) { - // wait for 1 tick - // we should not access the part list at this time - serverLevel.getServer().tell(new TickTask(0, this::validateConfig)); - } - } - - @Override - default void removedFromController(IMultiController controller) { - setAutoPullTest($ -> false); - if (isAutoPull()) { - getSlotList().clearInventory(0); - } - } - - IConfigurableSlotList getSlotList(); - - /** - * @return True if the passed stack is found as a configuration in any other stocking buses on the multiblock. - */ - boolean testConfiguredInOtherPart(@Nullable GenericStack config); - - /** - * Test for if any of our configured items are in another stocking bus on the multi - * we are attached to. Prevents dupes in certain situations. - */ - default void validateConfig() { - var slots = getSlotList(); - for (int i = 0; i < slots.getConfigurableSlots(); i++) { - var slot = slots.getConfigurableSlot(i); - if (slot.getConfig() != null) { - GenericStack configuredStack = slot.getConfig(); - if (testConfiguredInOtherPart(configuredStack)) { - slot.setConfig(null); - slot.setStock(null); - } - } - } - } - - int getMinStackSize(); - - void setMinStackSize(int newSize); - - int getTicksPerCycle(); - - void setTicksPerCycle(int newSize); -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/package-info.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/package-info.java new file mode 100644 index 00000000000..3842024cb17 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package com.gregtechceu.gtceu.integration.ae2.machine; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/package-info.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/package-info.java new file mode 100644 index 00000000000..6a9bf9491bd --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package com.gregtechceu.gtceu.integration.ae2.machine.trait; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlot.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlot.java deleted file mode 100644 index 98451f68a42..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlot.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import appeng.api.stacks.GenericStack; - -/** - * A slot that can be set to keep requesting. - */ -public interface IConfigurableSlot { - - GenericStack getConfig(); - - GenericStack getStock(); - - void setConfig(GenericStack val); - - void setStock(GenericStack val); - - IConfigurableSlot copy(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlotList.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlotList.java deleted file mode 100644 index 7de0beafeba..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/slot/IConfigurableSlotList.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.slot; - -import appeng.api.stacks.GenericStack; - -public interface IConfigurableSlotList { - - IConfigurableSlot getConfigurableSlot(int index); - - int getConfigurableSlots(); - - default boolean hasStackInConfig(GenericStack stack, boolean checkExternal) { - if (stack == null || stack.amount() <= 0) return false; - for (int i = 0; i < getConfigurableSlots(); i++) { - var slot = getConfigurableSlot(i); - GenericStack config = slot.getConfig(); - if (config != null && config.what().equals(stack.what())) { - return true; - } - } - return false; - } - - default void clearInventory(int startIndex) { - for (int i = startIndex; i < getConfigurableSlots(); i++) { - var slot = getConfigurableSlot(i); - slot.setConfig(null); - slot.setStock(null); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java deleted file mode 100644 index 792b47dc52b..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.utils; - -import com.gregtechceu.gtceu.utils.GTMath; - -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.fluids.FluidStack; - -import appeng.api.stacks.AEFluidKey; -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.GenericStack; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - -import static com.gregtechceu.gtceu.utils.GTMath.split; - -public class AEUtil { - - public static @Nullable GenericStack fromFluidStack(FluidStack stack) { - if (stack == null || stack.isEmpty()) return null; - var key = AEFluidKey.of(stack.getFluid(), stack.getTag()); - return new GenericStack(key, stack.getAmount()); - } - - public static FluidStack toFluidStack(GenericStack stack) { - var key = stack.what(); - if (key instanceof AEFluidKey fluidKey) { - return toFluidStack(fluidKey, stack.amount()); - } - return FluidStack.EMPTY; - } - - public static FluidStack toFluidStack(AEFluidKey key, long amount) { - return key.toStack(GTMath.saturatedCast(amount)); - } - - public static ItemStack[] toItemStacks(GenericStack stack) { - var key = stack.what(); - if (key instanceof AEItemKey itemKey) { - return toItemStacks(itemKey, stack.amount()); - } - return new ItemStack[0]; - } - - public static ItemStack[] toItemStacks(AEItemKey key, long amount) { - var ints = split(amount); - var itemStacks = new ItemStack[ints.length]; - for (int i = 0; i < ints.length; i++) { - itemStacks[i] = key.toStack(ints[i]); - } - return itemStacks; - } - - public static boolean matches(AEFluidKey key, FluidStack stack) { - return !stack.isEmpty() && key.getFluid().isSame(stack.getFluid()) && - Objects.equals(key.getTag(), stack.getTag()); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/package-info.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/package-info.java new file mode 100644 index 00000000000..027a5a91783 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package com.gregtechceu.gtceu.integration.ae2.utils; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file From c029a3842da98512864a7ea79527953828240785 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Tue, 24 Mar 2026 21:49:48 +0800 Subject: [PATCH 06/21] refactor ME stocking bus/hatch machines --- .../ae2/machine/MEStockingBusPartMachine.java | 455 +++++++-------- .../machine/MEStockingHatchPartMachine.java | 524 +++++++++--------- 2 files changed, 450 insertions(+), 529 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index 288dac498f5..5818ae06468 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -1,27 +1,34 @@ package com.gregtechceu.gtceu.integration.ae2.machine; -import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.IStackWatcher; +import appeng.api.networking.storage.IStorageWatcherNode; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.api.stacks.KeyCounter; +import appeng.api.storage.MEStorage; +import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.fancyconfigurator.AutoStockingFancyConfigurator; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.multiblock.IMEStockingPart; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlotList; - +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; +import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.gregtechceu.gtceu.utils.GTMath; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; import com.lowdragmc.lowdraglib.syncdata.annotation.DropSaved; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import lombok.Getter; +import lombok.Setter; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; @@ -30,30 +37,20 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.BlockHitResult; - -import appeng.api.config.Actionable; -import appeng.api.networking.IGrid; -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; -import appeng.api.storage.MEStorage; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import lombok.Getter; -import lombok.Setter; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; import java.util.Comparator; import java.util.PriorityQueue; -import java.util.function.Predicate; - -import javax.annotation.ParametersAreNonnullByDefault; -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class MEStockingBusPartMachine extends MEInputBusPartMachine implements IMEStockingPart { +public class MEStockingBusPartMachine extends MEBusPartMachine { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - MEStockingBusPartMachine.class, MEInputBusPartMachine.MANAGED_FIELD_HOLDER); + MEStockingBusPartMachine.class, + MEBusPartMachine.MANAGED_FIELD_HOLDER + ); + + protected final int slots; @DescSynced @Persisted @@ -65,177 +62,106 @@ public class MEStockingBusPartMachine extends MEInputBusPartMachine implements I @Persisted @DropSaved private int minStackSize = 1; - @Getter - @Setter - @Persisted - @DropSaved - private int ticksPerCycle = 40; - - @Setter - private Predicate autoPullTest; - - public MEStockingBusPartMachine(IMachineBlockEntity holder, Object... args) { - super(holder, args); - this.autoPullTest = $ -> false; - } - - ///////////////////////////////// - // ***** Machine LifeCycle ****// - ///////////////////////////////// - @Override - public void addedToController(IMultiController controller) { - super.addedToController(controller); - IMEStockingPart.super.addedToController(controller); - } - - @Override - public void removedFromController(IMultiController controller) { - IMEStockingPart.super.removedFromController(controller); - super.removedFromController(controller); - } - - @Override - protected NotifiableItemStackHandler createInventory(Object... args) { - this.aeItemHandler = new ExportOnlyAEStockingItemList(this, CONFIG_SIZE); - return this.aeItemHandler; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - ///////////////////////////////// - // ********** Sync ME *********// - ///////////////////////////////// - - @Override - public void autoIO() { - super.autoIO(); - if (ticksPerCycle == 0) ticksPerCycle = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; // Emergency Check to - // Avoid Crash loops. - if (getOffsetTimer() % ticksPerCycle == 0) { - if (autoPull) { - refreshList(); + private @UnknownNullability IStackWatcher storageWatcher; + private final GenericStackHandler configStacks; + + public MEStockingBusPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { + super(holder, tier, IO.IN, args); + this.slots = slots; + this.configStacks = new GenericStackHandler(slots) { + @Override + public void setStackInSlot(int slot, @Nullable GenericStack stack) { + GenericStack oldStack = getStackInSlot(slot); + super.setStackInSlot(slot, stack); + if (storageWatcher == null) return; + if (oldStack != null) { + storageWatcher.remove(oldStack.what()); + } + if (stack != null) { + storageWatcher.add(stack.what()); + } } - syncME(); - } + }; } @Override - protected void syncME() { - // Update the visual display for the fake items. This also is important for the item handler's - // getStackInSlot() method, as it uses the cached items set here. - MEStorage networkInv = this.getMainNode().getGrid().getStorageService().getInventory(); - for (ExportOnlyAEItemSlot slot : this.aeItemHandler.getInventory()) { - var config = slot.getConfig(); - if (config != null) { - // Try to fill the slot - var key = config.what(); - long extracted = networkInv.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, actionSource); - if (extracted >= minStackSize) { - slot.setStock(new GenericStack(key, extracted)); - continue; + protected GridNodeHost createNodeHost() { + GridNodeHost nodeHost = super.createNodeHost(); + nodeHost.getMainNode().addService(IStorageWatcherNode.class, new IStorageWatcherNode() { + @Override + public void updateWatcher(IStackWatcher newWatcher) { + storageWatcher = newWatcher; + for (int i = 0; i < configStacks.getSlots(); i++) { + GenericStack stack = configStacks.getStackInSlot(i); + if (stack != null) { + storageWatcher.add(stack.what()); + } } } - slot.setStock(null); - } - } - @Override - public void attachSideTabs(TabsWidget sideTabs) { - sideTabs.setMainTab(this); // removes the cover configurator, it's pointless and clashes with layout. - } - - @Override - protected void flushInventory() { - // no-op, nothing to send back to the network + @Override + public void onStackChange(AEKey what, long amount) { + getInventory().onContentsChanged(); + } + }); + return nodeHost; } @Override - public void setDistinct(boolean isDistinct) { - super.setDistinct(isDistinct); - if (!isRemote() && !isDistinct) { - // Ensure that our configured items won't match any other buses in the multiblock. - // Needed since we allow duplicates in distinct mode on, but not off - validateConfig(); - } + protected NotifiableItemStackHandler createInventory(Object... args) { + return new NotifiableItemStackHandler( + this, + getInventorySize(), + IO.IN, + IO.NONE, + MEStorageBackedItemHandler::new + ); } @Override - public IConfigurableSlotList getSlotList() { - return aeItemHandler; + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateInventorySubscription(); } @Override - public boolean testConfiguredInOtherPart(@Nullable GenericStack config) { - if (config == null) return false; - // In distinct mode, we don't need to check other buses since only one bus can run a recipe at a time. - if (!isFormed() || isDistinct()) return false; - - // Otherwise, we need to test for if the item is configured - // in any stocking bus in the multi (besides ourselves). - for (IMultiController controller : getControllers()) { - for (IMultiPart part : controller.getParts()) { - if (part instanceof MEStockingBusPartMachine bus) { - // We don't need to check for ourselves, as this case is handled elsewhere. - if (bus == this || bus.isDistinct()) continue; - if (bus.aeItemHandler.hasStackInConfig(config, false)) { - return true; - } - } - } + protected void updateInventorySubscription(Direction newFacing) { + IManagedGridNode node = nodeHost.getMainNode(); + if (isWorkingEnabled() && node.isActive() && isAutoPull()) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + return; + } + if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; } - return false; } @Override - public void setAutoPull(boolean autoPull) { - this.autoPull = autoPull; - if (!isRemote()) { - if (!this.autoPull) { - this.aeItemHandler.clearInventory(0); - } else if (updateMEStatus()) { - this.refreshList(); - updateInventorySubscription(); - } - } - } + public void autoIO() { + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; - /** - * Refresh the configuration list in auto-pull mode. - * Sets the config to the CONFIG_SIZE items with the highest amount in the ME system. - */ - private void refreshList() { - IGrid grid = this.getMainNode().getGrid(); - if (grid == null) { - aeItemHandler.clearInventory(0); - return; - } + int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + if (getOffsetTimer() % updateInterval != 0) return; - MEStorage networkStorage = grid.getStorageService().getInventory(); - var counter = networkStorage.getAvailableStacks(); + // Refresh the configuration list in auto-pull mode. + // Sets the config to the configStacks size items with the highest amount in the ME system. + KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); - // Use a PriorityQueue to sort the stacks on size, take the first CONFIG_SIZE + // Use a PriorityQueue to sort the stacks on size, take the first configStacks size // biggest stacks. - PriorityQueue> topItems = new PriorityQueue<>( - Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); + var topItems = new PriorityQueue<>(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); - for (Object2LongMap.Entry entry : counter) { + for (var entry : cachedInv) { + AEKey key = entry.getKey(); long amount = entry.getLongValue(); - AEKey what = entry.getKey(); - if (amount <= 0) continue; - if (!(what instanceof AEItemKey itemKey)) continue; + if (!(key instanceof AEItemKey)) continue; - long request = networkStorage.extract(what, amount, Actionable.SIMULATE, actionSource); - if (request == 0) continue; - - // Ensure that it is valid to configure with this stack - if (autoPullTest != null && !autoPullTest.test(new GenericStack(itemKey, amount))) continue; if (amount >= minStackSize) { - if (topItems.size() < CONFIG_SIZE) { + if (topItems.size() < configStacks.getSlots()) { topItems.offer(entry); } else if (amount > topItems.peek().getLongValue()) { topItems.poll(); @@ -244,43 +170,49 @@ private void refreshList() { } } - // Now, topItems is a PQ with CONFIG_SIZE highest amount items in the system. - int index; - int itemAmount = topItems.size(); - for (index = 0; index < CONFIG_SIZE; index++) { - if (topItems.isEmpty()) break; - Object2LongMap.Entry entry = topItems.poll(); - + // Now, topItems is a PQ with configStacks size highest amount items in the system. + for (int i = 0; i < configStacks.getSlots(); i++) { + var entry = topItems.poll(); + if (entry == null) { + configStacks.setStackInSlot(i, null); + continue; + } AEKey what = entry.getKey(); - long amount = entry.getLongValue(); - - // If we get here, the item has already been checked by the PQ. - long request = networkStorage.extract(what, amount, Actionable.SIMULATE, actionSource); - // Since we want our items to be displayed from highest to lowest, but poll() returns // the lowest first, we fill in the slots starting at itemAmount-1 - var slot = this.aeItemHandler.getInventory()[itemAmount - index - 1]; - slot.setConfig(new GenericStack(what, 1)); - slot.setStock(new GenericStack(what, request)); + configStacks.setStackInSlot(configStacks.getSlots() - i - 1, new GenericStack(what, 1)); } + } - aeItemHandler.clearInventory(index); + @Override + protected int getInventorySize() { + return slots; } - /////////////////////////////// - // ********** GUI ***********// - /////////////////////////////// + public void setAutoPull(boolean autoPull) { + this.autoPull = autoPull; + updateInventorySubscription(); + } @Override - public void attachConfigurators(ConfiguratorPanel configuratorPanel) { - IMEStockingPart.super.attachConfigurators(configuratorPanel); - super.attachConfigurators(configuratorPanel); - configuratorPanel.attachConfigurators(new AutoStockingFancyConfigurator(this)); + public void attachSideTabs(TabsWidget sideTabs) { + sideTabs.setMainTab(this); // removes the cover configurator, it's pointless and clashes with layout. } +// @Override +// public void attachConfigurators(ConfiguratorPanel configuratorPanel) { +// super.attachConfigurators(configuratorPanel); +// configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( +// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), +// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), +// this::isAutoPull, +// (clickData, pressed) -> setAutoPull(pressed)) +// .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); +// configuratorPanel.attachConfigurators(new AutoStockingFancyConfigurator(this)); +// } + @Override - protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, - BlockHitResult hitResult) { + protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, BlockHitResult hitResult) { if (!isRemote()) { setAutoPull(!autoPull); if (autoPull) { @@ -294,16 +226,9 @@ protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand return InteractionResult.sidedSuccess(isRemote()); } - //////////////////////////////// - // ****** Configuration ******// - //////////////////////////////// - - @Override - protected CompoundTag writeConfigToTag() { + protected CompoundTag writeConfig() { if (!autoPull) { - CompoundTag tag = super.writeConfigToTag(); - tag.putBoolean("AutoPull", false); - return tag; + } // if in auto-pull, no need to write actual configured slots, but still need to write the ghost circuit CompoundTag tag = new CompoundTag(); @@ -313,8 +238,7 @@ protected CompoundTag writeConfigToTag() { return tag; } - @Override - protected void readConfigFromTag(CompoundTag tag) { + protected void readConfig(CompoundTag tag) { if (tag.getBoolean("AutoPull")) { // if being set to auto-pull, no need to read the configured slots this.setAutoPull(true); @@ -323,84 +247,85 @@ protected void readConfigFromTag(CompoundTag tag) { } // set auto pull first to avoid issues with clearing the config after reading from the data stick this.setAutoPull(false); - super.readConfigFromTag(tag); + + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; } - private class ExportOnlyAEStockingItemList extends ExportOnlyAEItemList { + final class MEStorageBackedItemHandler extends CustomItemStackHandler { - public ExportOnlyAEStockingItemList(MetaMachine holder, int slots) { - super(holder, slots, ExportOnlyAEStockingItemSlot::new); + public MEStorageBackedItemHandler(int slots) { + super(slots); } @Override - public boolean isAutoPull() { - return autoPull; + public boolean isItemValid(int slot, ItemStack stack) { + AEItemKey key = getConfiguredKey(slot); + return key != null && key.matches(stack); } @Override - public boolean isStocking() { - return true; - } + public ItemStack getStackInSlot(int slot) { + validateSlotIndex(slot); - @Override - public boolean hasStackInConfig(GenericStack stack, boolean checkExternal) { - boolean inThisBus = super.hasStackInConfig(stack, false); - if (inThisBus) return true; - if (checkExternal) { - return testConfiguredInOtherPart(stack); - } - return false; - } - } + IGrid grid = getActiveGrid(); + if (grid == null) return ItemStack.EMPTY; - private class ExportOnlyAEStockingItemSlot extends ExportOnlyAEItemSlot { + AEItemKey key = getConfiguredKey(slot); + if (key == null) return ItemStack.EMPTY; - public ExportOnlyAEStockingItemSlot() { - super(); + KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); + long existing = cachedInv.get(key); + + return key.toStack(GTMath.saturatedCast(existing)); } - public ExportOnlyAEStockingItemSlot(@Nullable GenericStack config, @Nullable GenericStack stock) { - super(config, stock); + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + return stack; } @Override public ItemStack extractItem(int slot, int amount, boolean simulate) { - if (slot == 0 && this.stock != null) { - if (this.config != null) { - // Extract the items from the real net to either validate (simulate) - // or extract (modulate) when this is called - if (!isOnline()) return ItemStack.EMPTY; - MEStorage aeNetwork = getMainNode().getGrid().getStorageService().getInventory(); - - Actionable action = simulate ? Actionable.SIMULATE : Actionable.MODULATE; - var key = config.what(); - long extracted = aeNetwork.extract(key, amount, action, actionSource); - - if (extracted > 0) { - ItemStack resultStack = key instanceof AEItemKey itemKey ? - itemKey.toStack((int) extracted) : ItemStack.EMPTY; - if (!simulate) { - // may as well update the display here - this.stock = ExportOnlyAESlot.copy(stock, stock.amount() - extracted); - if (this.stock.amount() == 0) { - this.stock = null; - } - if (this.onContentsChanged != null) { - this.onContentsChanged.run(); - } - } - return resultStack; - } - } - } - return ItemStack.EMPTY; + if (amount <= 0) return ItemStack.EMPTY; + + validateSlotIndex(slot); + + IGrid grid = getActiveGrid(); + if (grid == null) return ItemStack.EMPTY; + + AEItemKey key = getConfiguredKey(slot); + if (key == null) return ItemStack.EMPTY; + + // Extract the items from the real net to either validate (simulate) + // or extract (modulate) when this is called + MEStorage networkInv = grid.getStorageService().getInventory(); + long extracted = networkInv.extract( + key, + amount, + simulate ? Actionable.SIMULATE : Actionable.MODULATE, + actionSource + ); + + return key.toStack(Math.toIntExact(extracted)); } - @Override - public ExportOnlyAEStockingItemSlot copy() { - return new ExportOnlyAEStockingItemSlot( - this.config == null ? null : copy(this.config), - this.stock == null ? null : copy(this.stock)); + private @Nullable AEItemKey getConfiguredKey(int slot) { + GenericStack configuredStack = configStacks.getStackInSlot(slot); + if (configuredStack == null) return null; + + assert configuredStack.what() instanceof AEItemKey; + return (AEItemKey) configuredStack.what(); + } + + private @Nullable IGrid getActiveGrid() { + IManagedGridNode gridNode = nodeHost.getMainNode(); + if (!gridNode.isActive()) return null; + + return gridNode.getGrid(); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index 8b07ee7ad05..8f9f0301aa7 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -1,62 +1,60 @@ package com.gregtechceu.gtceu.integration.ae2.machine; -import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import appeng.api.config.Actionable; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.IStackWatcher; +import appeng.api.networking.storage.IStorageWatcherNode; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.api.stacks.KeyCounter; +import appeng.api.storage.MEStorage; +import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.fancyconfigurator.AutoStockingFancyConfigurator; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; -import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.multiblock.IMEStockingPart; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlotList; -import com.gregtechceu.gtceu.integration.ae2.utils.AEUtil; - +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; +import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.gregtechceu.gtceu.utils.GTMath; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; import com.lowdragmc.lowdraglib.syncdata.annotation.DropSaved; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import lombok.Getter; +import lombok.Setter; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.fluids.FluidStack; - -import appeng.api.config.Actionable; -import appeng.api.networking.IGrid; -import appeng.api.stacks.AEFluidKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; -import appeng.api.storage.MEStorage; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import lombok.Getter; -import lombok.Setter; +import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import java.util.ArrayList; import java.util.Comparator; import java.util.PriorityQueue; -import java.util.function.Predicate; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class MEStockingHatchPartMachine extends MEInputHatchPartMachine implements IMEStockingPart { +public class MEStockingHatchPartMachine extends MEHatchPartMachine implements IDataStickInteractable { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - MEStockingHatchPartMachine.class, MEInputHatchPartMachine.MANAGED_FIELD_HOLDER); + MEStockingHatchPartMachine.class, + MEHatchPartMachine.MANAGED_FIELD_HOLDER + ); - private static final int CONFIG_SIZE = 16; + protected final int slots; @DescSynced @Persisted @@ -69,202 +67,130 @@ public class MEStockingHatchPartMachine extends MEInputHatchPartMachine implemen @DropSaved private int minStackSize = 1; - @Getter - @Setter - @Persisted - @DropSaved - private int ticksPerCycle = 40; - - @Setter - private Predicate autoPullTest; - - public MEStockingHatchPartMachine(IMachineBlockEntity holder, Object... args) { - super(holder, args); - this.autoPullTest = $ -> false; - } + private @UnknownNullability IStackWatcher storageWatcher; - ///////////////////////////////// - // ***** Machine LifeCycle ****// - ///////////////////////////////// - - @Override - public void addedToController(IMultiController controller) { - super.addedToController(controller); - IMEStockingPart.super.addedToController(controller); - } - - @Override - public void removedFromController(IMultiController controller) { - IMEStockingPart.super.removedFromController(controller); - super.removedFromController(controller); - } - - @Override - protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object... args) { - this.aeFluidHandler = new ExportOnlyAEStockingFluidList(this, CONFIG_SIZE); - return this.aeFluidHandler; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - ///////////////////////////////// - // ********** Sync ME *********// - ///////////////////////////////// - - @Override - public void autoIO() { - super.autoIO(); - if (ticksPerCycle == 0) ticksPerCycle = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; // Emergency Check to - // Avoid Crash loops. - if (getOffsetTimer() % ticksPerCycle == 0) { - if (autoPull) { - refreshList(); + @Persisted + protected final GenericStackHandler configStacks; + + public MEStockingHatchPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { + super(holder, tier, IO.IN, -1, slots, args); + this.slots = slots; + this.configStacks = new GenericStackHandler(slots) { + @Override + public void setStackInSlot(int slot, @Nullable GenericStack stack) { + GenericStack oldStack = getStackInSlot(slot); + super.setStackInSlot(slot, stack); + if (storageWatcher != null) { + if (oldStack != null) { + storageWatcher.remove(oldStack.what()); + } + if (stack != null) { + storageWatcher.add(stack.what()); + } + } + tank.onContentsChanged(); } - syncME(); - } + }; } @Override - protected void syncME() { - MEStorage networkInv = this.getMainNode().getGrid().getStorageService().getInventory(); - for (ExportOnlyAEFluidSlot slot : aeFluidHandler.getInventory()) { - var config = slot.getConfig(); - if (config != null) { - // Try to fill the slot - var key = config.what(); - long extracted = networkInv.extract(key, Long.MAX_VALUE, Actionable.SIMULATE, actionSource); - if (extracted >= minStackSize) { - slot.setStock(new GenericStack(key, extracted)); - continue; + protected GridNodeHost createNodeHost() { + GridNodeHost nodeHost = super.createNodeHost(); + nodeHost.getMainNode().addService(IStorageWatcherNode.class, new IStorageWatcherNode() { + @Override + public void updateWatcher(IStackWatcher newWatcher) { + storageWatcher = newWatcher; + for (int i = 0; i < configStacks.getSlots(); i++) { + GenericStack stack = configStacks.getStackInSlot(i); + if (stack != null) { + storageWatcher.add(stack.what()); + } } } - slot.setStock(null); - } - } - @Override - public void attachSideTabs(TabsWidget sideTabs) { - sideTabs.setMainTab(this); // removes the cover configurator, it's pointless and clashes with layout. + @Override + public void onStackChange(AEKey what, long amount) { + tank.onContentsChanged(); + } + }); + return nodeHost; } @Override - protected void flushInventory() { - // no-op, nothing to send back to the network + protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object... args) { + var storages = new ArrayList(slots); + for (int i = 0; i < slots; i++) { + storages.add(new MEStorageBackedFluidStorage(i)); + } + return new NotifiableFluidTank(this, storages, IO.IN, IO.NONE); } @Override - public IConfigurableSlotList getSlotList() { - return aeFluidHandler; + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateTankSubscription(); } @Override - public boolean testConfiguredInOtherPart(@Nullable GenericStack config) { - if (config == null) return false; - if (!isFormed()) return false; - - for (IMultiController controller : getControllers()) { - for (IMultiPart part : controller.getParts()) { - if (part instanceof MEStockingHatchPartMachine hatch) { - if (hatch == this) continue; - if (hatch.aeFluidHandler.hasStackInConfig(config, false)) { - return true; - } - } - } + protected void updateTankSubscription(Direction newFacing) { + IManagedGridNode node = nodeHost.getMainNode(); + if (isWorkingEnabled() && node.isActive() && isAutoPull()) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + return; } - return false; - } - - @Override - public void setAutoPull(boolean autoPull) { - this.autoPull = autoPull; - if (!isRemote()) { - if (!this.autoPull) { - this.aeFluidHandler.clearInventory(0); - } else if (updateMEStatus()) { - this.refreshList(); - updateTankSubscription(); - } + if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; } } - private void refreshList() { - IGrid grid = this.getMainNode().getGrid(); - if (grid == null) { - aeFluidHandler.clearInventory(0); - return; - } + @Override + protected void autoIO() { + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; - MEStorage networkStorage = grid.getStorageService().getInventory(); - var counter = networkStorage.getAvailableStacks(); + int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + if (getOffsetTimer() % updateInterval != 0) return; - // Use a PriorityQueue to sort the stacks on size, take the first CONFIG_SIZE - // biggest stacks. - PriorityQueue> topFluids = new PriorityQueue<>( - Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); + KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); + var topFluids = new PriorityQueue<>(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); - for (Object2LongMap.Entry entry : counter) { + for (var entry : cachedInv) { + AEKey key = entry.getKey(); long amount = entry.getLongValue(); - AEKey what = entry.getKey(); - - if (amount <= 0) continue; - if (!(what instanceof AEFluidKey fluidKey)) continue; - - long request = networkStorage.extract(what, amount, Actionable.SIMULATE, actionSource); - if (request == 0) continue; - - // Ensure that it is valid to configure with this stack - if (autoPullTest != null && !autoPullTest.test(new GenericStack(fluidKey, amount))) continue; - if (amount >= minStackSize) { - if (topFluids.size() < CONFIG_SIZE) { - topFluids.offer(entry); - } else if (amount > topFluids.peek().getLongValue()) { - topFluids.poll(); - topFluids.offer(entry); - } - } - } - // Now, topFluids is a PQ with CONFIG_SIZE highest amount fluids in the system. - int index; - int fluidAmount = topFluids.size(); - for (index = 0; index < CONFIG_SIZE; index++) { - if (topFluids.isEmpty()) break; - Object2LongMap.Entry entry = topFluids.poll(); - AEKey what = entry.getKey(); - long amount = entry.getLongValue(); + if (!(key instanceof AEFluidKey)) continue; + if (amount < minStackSize) continue; - // If we get here, the fluid has already been checked by the PQ. - long request = networkStorage.extract(what, amount, Actionable.SIMULATE, actionSource); + if (topFluids.size() < configStacks.getSlots()) { + topFluids.offer(entry); + } else if (amount > topFluids.peek().getLongValue()) { + topFluids.poll(); + topFluids.offer(entry); + } + } - // Since we want our fluids to be displayed from highest to lowest, but poll() returns - // the lowest first, we fill in the slots starting at fluidAmount-1 - var slot = this.aeFluidHandler.getInventory()[fluidAmount - index - 1]; - slot.setConfig(new GenericStack(what, 1)); - slot.setStock(new GenericStack(what, request)); + for (int i = 0; i < configStacks.getSlots(); i++) { + configStacks.setStackInSlot(i, null); } - aeFluidHandler.clearInventory(index); + for (int i = 0; i < configStacks.getSlots(); i++) { + var entry = topFluids.poll(); + if (entry == null) break; + configStacks.setStackInSlot(configStacks.getSlots() - i - 1, new GenericStack(entry.getKey(), 1)); + } } - /////////////////////////////// - // ********** GUI ***********// - /////////////////////////////// + public void setAutoPull(boolean autoPull) { + this.autoPull = autoPull; + updateTankSubscription(); + } @Override - public void attachConfigurators(ConfiguratorPanel configuratorPanel) { - IMEStockingPart.super.attachConfigurators(configuratorPanel); - super.attachConfigurators(configuratorPanel); - configuratorPanel.attachConfigurators(new AutoStockingFancyConfigurator(this)); + public void attachSideTabs(TabsWidget sideTabs) { + sideTabs.setMainTab(this); } - //////////////////////////////// - // ******* Interaction *******// - //////////////////////////////// - @Override protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, BlockHitResult hitResult) { @@ -281,111 +207,181 @@ protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand return InteractionResult.sidedSuccess(isRemote()); } - //////////////////////////////// - // ****** Configuration ******// - //////////////////////////////// + @Override + public InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { + if (!isRemote()) { + CompoundTag tag = new CompoundTag(); + tag.put("MEInputHatch", writeConfig()); + dataStick.setTag(tag); + dataStick.setHoverName(Component.translatable("gtceu.machine.me.fluid_import.data_stick.name")); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); + } + return InteractionResult.SUCCESS; + } @Override - protected CompoundTag writeConfigToTag() { - if (!autoPull) { - CompoundTag tag = super.writeConfigToTag(); - tag.putBoolean("AutoPull", false); - return tag; + public InteractionResult onDataStickUse(Player player, ItemStack dataStick) { + CompoundTag tag = dataStick.getTag(); + if (tag == null || !tag.contains("MEInputHatch")) { + return InteractionResult.PASS; } - // if in auto-pull, no need to write actual configured slots, but still need to write the ghost circuit + if (!isRemote()) { + readConfig(tag.getCompound("MEInputHatch")); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); + } + return InteractionResult.sidedSuccess(isRemote()); + } + + protected CompoundTag writeConfig() { CompoundTag tag = new CompoundTag(); - tag.putBoolean("AutoPull", true); - tag.putByte("GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0))); + tag.putBoolean("AutoPull", autoPull); + if (!autoPull) { + tag.put("ConfigStacks", configStacks.serializeNBT()); + } + tag.putByte( + "GhostCircuit", + (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) + ); return tag; } - @Override - protected void readConfigFromTag(CompoundTag tag) { - if (tag.getBoolean("AutoPull")) { - // if being set to auto-pull, no need to read the configured slots - this.setAutoPull(true); + protected void readConfig(CompoundTag tag) { + setAutoPull(tag.getBoolean("AutoPull")); + if (!autoPull && tag.contains("ConfigStacks")) { + var oldStacks = captureConfiguredKeys(); + configStacks.deserializeNBT(tag.getCompound("ConfigStacks")); + syncStorageWatcher(oldStacks); + tank.onContentsChanged(); + } + if (tag.contains("GhostCircuit")) { circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); - return; } - // set auto pull first to avoid issues with clearing the config after reading from the data stick - this.setAutoPull(false); - super.readConfigFromTag(tag); } - private class ExportOnlyAEStockingFluidList extends ExportOnlyAEFluidList { + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } - public ExportOnlyAEStockingFluidList(MetaMachine holder, int slots) { - super(holder, slots, ExportOnlyAEStockingFluidSlot::new); + private @Nullable AEFluidKey getConfiguredKey(int slot) { + GenericStack configuredStack = configStacks.getStackInSlot(slot); + if (configuredStack == null) return null; + + assert configuredStack.what() instanceof AEFluidKey; + return (AEFluidKey) configuredStack.what(); + } + + private @Nullable IGrid getActiveGrid() { + IManagedGridNode gridNode = nodeHost.getMainNode(); + if (!gridNode.isActive()) return null; + return gridNode.getGrid(); + } + + private GenericStack[] captureConfiguredKeys() { + GenericStack[] stacks = new GenericStack[configStacks.getSlots()]; + for (int i = 0; i < stacks.length; i++) { + stacks[i] = configStacks.getStackInSlot(i); } + return stacks; + } - @Override - public boolean isAutoPull() { - return autoPull; + private void syncStorageWatcher(GenericStack[] oldStacks) { + if (storageWatcher == null) return; + + for (GenericStack stack : oldStacks) { + if (stack != null) { + storageWatcher.remove(stack.what()); + } + } + for (int i = 0; i < configStacks.getSlots(); i++) { + GenericStack stack = configStacks.getStackInSlot(i); + if (stack != null) { + storageWatcher.add(stack.what()); + } + } + } + + private final class MEStorageBackedFluidStorage extends CustomFluidTank { + + private final int slot; + + private MEStorageBackedFluidStorage(int slot) { + super(Integer.MAX_VALUE); + this.slot = slot; } @Override - public boolean isStocking() { - return true; + public void setFluid(FluidStack stack) { + // no-op } @Override - public boolean hasStackInConfig(GenericStack stack, boolean checkExternal) { - boolean inThisHatch = super.hasStackInConfig(stack, false); - if (inThisHatch) return true; - if (checkExternal) { - return testConfiguredInOtherPart(stack); - } - return false; + public FluidStack getFluid() { + IGrid grid = getActiveGrid(); + if (grid == null) return FluidStack.EMPTY; + + AEFluidKey key = getConfiguredKey(slot); + if (key == null) return FluidStack.EMPTY; + + long existing = grid.getStorageService().getCachedInventory().get(key); + if (existing <= 0) return FluidStack.EMPTY; + + return key.toStack(GTMath.saturatedCast(existing)); } - } - private class ExportOnlyAEStockingFluidSlot extends ExportOnlyAEFluidSlot { + @Override + public boolean isFluidValid(FluidStack stack) { + AEFluidKey key = getConfiguredKey(slot); + return key != null && key.matches(stack); + } - public ExportOnlyAEStockingFluidSlot() { - super(); + @Override + public int fill(FluidStack resource, FluidAction action) { + return 0; } - public ExportOnlyAEStockingFluidSlot(@Nullable GenericStack config, @Nullable GenericStack stock) { - super(config, stock); + @Override + public boolean supportsFill(int tank) { + return false; } @Override - public ExportOnlyAEFluidSlot copy() { - return new ExportOnlyAEStockingFluidSlot( - this.config == null ? null : copy(this.config), - this.stock == null ? null : copy(this.stock)); + public FluidStack drain(FluidStack resource, FluidAction action) { + if (resource.isEmpty()) return FluidStack.EMPTY; + + AEFluidKey key = getConfiguredKey(slot); + if (key == null || !key.matches(resource)) return FluidStack.EMPTY; + + return extract(key, resource.getAmount(), action); } @Override public FluidStack drain(int maxDrain, FluidAction action) { - if (this.stock != null && this.config != null) { - // Extract the items from the real net to either validate (simulate) - // or extract (modulate) when this is called - if (!isOnline()) return FluidStack.EMPTY; - MEStorage aeNetwork = getMainNode().getGrid().getStorageService().getInventory(); - - Actionable actionable = action.simulate() ? Actionable.SIMULATE : Actionable.MODULATE; - var key = config.what(); - long extracted = aeNetwork.extract(key, maxDrain, actionable, actionSource); - - if (extracted > 0) { - FluidStack resultStack = key instanceof AEFluidKey fluidKey ? - AEUtil.toFluidStack(fluidKey, extracted) : FluidStack.EMPTY; - if (action.execute()) { - // may as well update the display here - this.stock = ExportOnlyAESlot.copy(stock, stock.amount() - extracted); - if (this.stock.amount() == 0) { - this.stock = null; - } - if (this.onContentsChanged != null) { - this.onContentsChanged.run(); - } - } - return resultStack; - } + if (maxDrain <= 0) return FluidStack.EMPTY; + + AEFluidKey key = getConfiguredKey(slot); + if (key == null) return FluidStack.EMPTY; + + return extract(key, maxDrain, action); + } + + private FluidStack extract(AEFluidKey key, int amount, FluidAction action) { + IGrid grid = getActiveGrid(); + if (grid == null) return FluidStack.EMPTY; + + MEStorage networkInv = grid.getStorageService().getInventory(); + long extracted = networkInv.extract( + key, + amount, + action.simulate() ? Actionable.SIMULATE : Actionable.MODULATE, + actionSource + ); + if (extracted <= 0) return FluidStack.EMPTY; + + if (action.execute()) { + onContentsChanged(); } - return FluidStack.EMPTY; + return key.toStack(Math.toIntExact(extracted)); } } } From 991fa1bb2d74f0a2aa678f5d38b455b11707c45e Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Wed, 25 Mar 2026 20:00:36 +0800 Subject: [PATCH 07/21] refactor ME part machine subscription logic --- .../ae2/machine/MEBusPartMachine.java | 16 ++++++++++++ .../ae2/machine/MEHatchPartMachine.java | 16 ++++++++++++ .../ae2/machine/MEInputBusPartMachine.java | 25 +++++++------------ .../ae2/machine/MEInputHatchPartMachine.java | 23 +++++++---------- .../ae2/utils/GenericStackHandler.java | 10 ++++++++ 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java index 69e05fe3c6c..96f7064e6d2 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java @@ -10,6 +10,7 @@ import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; +import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.core.Direction; import org.jetbrains.annotations.Nullable; @@ -44,6 +45,21 @@ protected GridNodeHost createNodeHost() { return host; } + protected boolean shouldUpdateSubscription(Direction newFacing) { + return isWorkingEnabled() && ((io.support(IO.OUT) && !getInventory().isEmpty()) || io.support(IO.IN)) && + GTTransferUtils.hasAdjacentItemHandler(getLevel(), getPos(), newFacing); + } + + @Override + protected void updateInventorySubscription(Direction newFacing) { + if (shouldUpdateSubscription(newFacing)) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + } else if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; + } + } + @Override public @Nullable IGridNode getActionableNode() { return nodeHost.getMainNode().getNode(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java index 8dfeeb12d68..6dfb65c37c3 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java @@ -10,6 +10,7 @@ import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; +import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.core.Direction; import org.jetbrains.annotations.Nullable; @@ -42,6 +43,21 @@ protected GridNodeHost createNodeHost() { return host; } + protected boolean shouldUpdateSubscription(Direction newFacing) { + return isWorkingEnabled() && ((io.support(IO.OUT) && !tank.isEmpty()) || io.support(IO.IN)) && + GTTransferUtils.hasAdjacentFluidHandler(getLevel(), getPos(), newFacing); + } + + @Override + protected void updateTankSubscription(Direction newFacing) { + if (shouldUpdateSubscription(newFacing)) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + } else if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; + } + } + @Override public @Nullable IGridNode getActionableNode() { return nodeHost.getMainNode().getNode(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java index 2c5c93ab86c..b1ca0f62811 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java @@ -32,12 +32,12 @@ public class MEInputBusPartMachine extends MEBusPartMachine implements IDataStic ); @Persisted - protected final GenericStackHandler configStacks; + protected final GenericStackHandler configHandler; protected final int slots; public MEInputBusPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { super(holder, tier, IO.IN, args); - this.configStacks = new GenericStackHandler(slots); + this.configHandler = new GenericStackHandler(slots); this.slots = slots; } @@ -63,16 +63,9 @@ protected int getStackLimit(int slot, ItemStack stack) { } @Override - protected void updateInventorySubscription(Direction newFacing) { + protected boolean shouldUpdateSubscription(Direction newFacing) { IManagedGridNode node = nodeHost.getMainNode(); - if (isWorkingEnabled() && node.isActive()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - return; - } - if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } + return isWorkingEnabled() && node.isActive(); } @Override @@ -96,8 +89,8 @@ protected void autoIO() { MEStorage networkInv = grid.getStorageService().getInventory(); NotifiableItemStackHandler inventory = getInventory(); - for (int i = 0; i < configStacks.getSlots(); i++) { - GenericStack configStack = configStacks.getStackInSlot(i); + for (int i = 0; i < configHandler.getSlots(); i++) { + GenericStack configStack = configHandler.getStackInSlot(i); if (configStack == null) continue; AEItemKey configKey = (AEItemKey) configStack.what(); long configAmount = configStack.amount(); @@ -189,7 +182,7 @@ public final InteractionResult onDataStickUse(Player player, ItemStack dataStick protected CompoundTag writeConfig() { CompoundTag tag = new CompoundTag(); - tag.put("ConfigStacks", configStacks.serializeNBT()); + tag.put("ConfigHandler", configHandler.serializeNBT()); tag.putByte( "GhostCircuit", (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) @@ -199,8 +192,8 @@ protected CompoundTag writeConfig() { } protected void readConfig(CompoundTag tag) { - if (tag.contains("ConfigStacks")) { - configStacks.deserializeNBT(tag.getCompound("ConfigStacks")); + if (tag.contains("ConfigHandler")) { + configHandler.deserializeNBT(tag.getCompound("ConfigHandler")); } if (tag.contains("GhostCircuit")) { circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java index 6a81ec37849..565f6a0173b 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java @@ -34,11 +34,11 @@ public class MEInputHatchPartMachine extends MEHatchPartMachine implements IData ); @Persisted - protected final GenericStackHandler configStacks; + protected final GenericStackHandler configHandler; public MEInputHatchPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { super(holder, tier, IO.IN, -1, slots, args); - this.configStacks = new GenericStackHandler(slots); + this.configHandler = new GenericStackHandler(slots); } @Override @@ -47,14 +47,9 @@ protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object. } @Override - protected void updateTankSubscription(Direction newFacing) { + protected boolean shouldUpdateSubscription(Direction newFacing) { IManagedGridNode node = nodeHost.getMainNode(); - if (isWorkingEnabled() && node.isActive()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - } else if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } + return isWorkingEnabled() && node.isActive(); } @Override @@ -68,8 +63,8 @@ protected void autoIO() { MEStorage networkInv = grid.getStorageService().getInventory(); FluidTank[] tanks = tank.getStorages(); - for (int i = 0; i < configStacks.getSlots(); i++) { - GenericStack configStack = configStacks.getStackInSlot(i); + for (int i = 0; i < configHandler.getSlots(); i++) { + GenericStack configStack = configHandler.getStackInSlot(i); if (configStack == null) continue; AEFluidKey configKey = (AEFluidKey) configStack.what(); long configAmount = configStack.amount(); @@ -168,7 +163,7 @@ public final InteractionResult onDataStickUse(Player player, ItemStack dataStick protected CompoundTag writeConfig() { CompoundTag tag = new CompoundTag(); - tag.put("ConfigStacks", configStacks.serializeNBT()); + tag.put("ConfigHandler", configHandler.serializeNBT()); tag.putByte( "GhostCircuit", (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) @@ -177,8 +172,8 @@ protected CompoundTag writeConfig() { } protected void readConfig(CompoundTag tag) { - if (tag.contains("ConfigStacks")) { - configStacks.deserializeNBT(tag.getCompound("ConfigStacks")); + if (tag.contains("ConfigHandler")) { + configHandler.deserializeNBT(tag.getCompound("ConfigHandler")); } if (tag.contains("GhostCircuit")) { circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java index 0e7e487a634..ac83dee1ad5 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/GenericStackHandler.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.integration.ae2.utils; +import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; @@ -35,12 +36,21 @@ public int getSlots() { return stacks[slot]; } + public @Nullable AEKey getKeyInSlot(int slot) { + GenericStack stack = getStackInSlot(slot); + return stack != null ? stack.what() : null; + } + public void setStackInSlot(int slot, @Nullable GenericStack stack) { validateSlotIndex(slot); stacks[slot] = stack; onContentsChanged(slot); } + public void setKeyInSlot(int slot, @Nullable AEKey key) { + setStackInSlot(slot, key != null ? new GenericStack(key, 1) : null); + } + protected void validateSlotIndex(int slot) { if (slot < 0 || slot >= stacks.length) throw new RuntimeException("Slot " + slot + " not in valid range - [0," + stacks.length + ")"); From d90b9c6d058e9216deaaff0199c9fe730c6bb8d4 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Thu, 26 Mar 2026 00:05:44 +0800 Subject: [PATCH 08/21] refactor ME data stick config handling --- .../feature/IDataStickConfigurable.java | 49 ++++ .../ae2/machine/MEInputBusPartMachine.java | 63 ++-- .../ae2/machine/MEInputHatchPartMachine.java | 58 ++-- .../ae2/machine/MEStockingBusPartMachine.java | 201 ++++--------- .../machine/MEStockingHatchPartMachine.java | 273 +++++------------- .../integration/ae2/utils/MEConfigUtil.java | 60 ++++ .../ae2/utils/StockingConfigHandler.java | 120 ++++++++ 7 files changed, 389 insertions(+), 435 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDataStickConfigurable.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDataStickConfigurable.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDataStickConfigurable.java new file mode 100644 index 00000000000..e12409f027c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDataStickConfigurable.java @@ -0,0 +1,49 @@ +package com.gregtechceu.gtceu.api.machine.feature; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +public interface IDataStickConfigurable extends IMachineFeature, IDataStickInteractable { + + @Override + default InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { + if (!self().isRemote()) { + CompoundTag root = dataStick.getOrCreateTag(); + CompoundTag config = new CompoundTag(); + writeConfig(config); + root.put(getConfigKey(), config); + dataStick.setHoverName(getConfigName()); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); + } + return InteractionResult.SUCCESS; + } + + @Override + default InteractionResult onDataStickUse(Player player, ItemStack dataStick) { + String tagKey = getConfigKey(); + CompoundTag root = dataStick.getTag(); + if (root == null || !root.contains(tagKey)) { + return InteractionResult.PASS; + } + if (!self().isRemote()) { + readConfig(root.getCompound(tagKey)); + player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); + } + return InteractionResult.sidedSuccess(self().isRemote()); + } + + default String getConfigKey() { + return getClass().getSimpleName(); + } + + default Component getConfigName() { + return Component.literal(getConfigKey()); + } + + void writeConfig(CompoundTag tag); + + void readConfig(CompoundTag tag); +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java index b1ca0f62811..81185b8ca94 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java @@ -9,22 +9,23 @@ import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -public class MEInputBusPartMachine extends MEBusPartMachine implements IDataStickInteractable { +public class MEInputBusPartMachine extends MEBusPartMachine implements IDataStickConfigurable { + + static final String CONFIG_KEY = "MEInputBus"; + static final Component CONFIG_NAME = Component.translatable("gtceu.machine.me.item_import.data_stick.name"); protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( MEInputBusPartMachine.class, @@ -156,51 +157,27 @@ public void onMachineRemoved() { // } @Override - public final InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { - if (!isRemote()) { - CompoundTag tag = new CompoundTag(); - tag.put("MEInputBus", writeConfig()); - dataStick.setTag(tag); - dataStick.setHoverName(Component.translatable("gtceu.machine.me.item_import.data_stick.name")); - player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); - } - return InteractionResult.SUCCESS; + public String getConfigKey() { + return CONFIG_KEY; } @Override - public final InteractionResult onDataStickUse(Player player, ItemStack dataStick) { - CompoundTag tag = dataStick.getTag(); - if (tag == null || !tag.contains("MEInputBus")) { - return InteractionResult.PASS; - } - if (!isRemote()) { - readConfig(tag.getCompound("MEInputBus")); - player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); - } - return InteractionResult.sidedSuccess(isRemote()); + public Component getConfigName() { + return CONFIG_NAME; } - protected CompoundTag writeConfig() { - CompoundTag tag = new CompoundTag(); - tag.put("ConfigHandler", configHandler.serializeNBT()); - tag.putByte( - "GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) - ); - tag.putBoolean("DistinctBuses", isDistinct()); - return tag; + @Override + public void writeConfig(CompoundTag tag) { + MEConfigUtil.writeConfigHandler(tag, configHandler); + MEConfigUtil.writeGhostCircuit(tag, circuitInventory); + MEConfigUtil.writeDistinctBuses(tag, isDistinct()); } - protected void readConfig(CompoundTag tag) { - if (tag.contains("ConfigHandler")) { - configHandler.deserializeNBT(tag.getCompound("ConfigHandler")); - } - if (tag.contains("GhostCircuit")) { - circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); - } - if (tag.contains("DistinctBuses")) { - setDistinct(tag.getBoolean("DistinctBuses")); - } + @Override + public void readConfig(CompoundTag tag) { + MEConfigUtil.readConfigHandler(tag, configHandler); + MEConfigUtil.readGhostCircuit(tag, circuitInventory); + MEConfigUtil.readDistinctBuses(tag, this::setDistinct); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java index 565f6a0173b..45666bc8852 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java @@ -9,24 +9,24 @@ import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; -import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.templates.FluidTank; -public class MEInputHatchPartMachine extends MEHatchPartMachine implements IDataStickInteractable { +public class MEInputHatchPartMachine extends MEHatchPartMachine implements IDataStickConfigurable { + + static final String CONFIG_KEY = "MEInputHatch"; + static final Component CONFIG_NAME = Component.translatable("gtceu.machine.me.fluid_import.data_stick.name"); protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( MEInputHatchPartMachine.class, @@ -137,47 +137,25 @@ public void onMachineRemoved() { // } @Override - public final InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { - if (!isRemote()) { - CompoundTag tag = new CompoundTag(); - tag.put("MEInputHatch", writeConfig()); - dataStick.setTag(tag); - dataStick.setHoverName(Component.translatable("gtceu.machine.me.fluid_import.data_stick.name")); - player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); - } - return InteractionResult.SUCCESS; + public String getConfigKey() { + return CONFIG_KEY; } @Override - public final InteractionResult onDataStickUse(Player player, ItemStack dataStick) { - CompoundTag tag = dataStick.getTag(); - if (tag == null || !tag.contains("MEInputHatch")) { - return InteractionResult.PASS; - } - if (!isRemote()) { - readConfig(tag.getCompound("MEInputHatch")); - player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); - } - return InteractionResult.sidedSuccess(isRemote()); + public Component getConfigName() { + return CONFIG_NAME; } - protected CompoundTag writeConfig() { - CompoundTag tag = new CompoundTag(); - tag.put("ConfigHandler", configHandler.serializeNBT()); - tag.putByte( - "GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) - ); - return tag; + @Override + public void writeConfig(CompoundTag tag) { + MEConfigUtil.writeConfigHandler(tag, configHandler); + MEConfigUtil.writeGhostCircuit(tag, circuitInventory); } - protected void readConfig(CompoundTag tag) { - if (tag.contains("ConfigHandler")) { - configHandler.deserializeNBT(tag.getCompound("ConfigHandler")); - } - if (tag.contains("GhostCircuit")) { - circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); - } + @Override + public void readConfig(CompoundTag tag) { + MEConfigUtil.readConfigHandler(tag, configHandler); + MEConfigUtil.readGhostCircuit(tag, circuitInventory); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index 5818ae06468..4774cc3832b 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -4,29 +4,23 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridNodeListener; import appeng.api.networking.IManagedGridNode; -import appeng.api.networking.IStackWatcher; import appeng.api.networking.storage.IStorageWatcherNode; import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; import appeng.api.stacks.KeyCounter; import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; -import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; +import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; import com.gregtechceu.gtceu.utils.GTMath; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.DropSaved; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import it.unimi.dsi.fastutil.objects.Object2LongMap; import lombok.Getter; import lombok.Setter; import net.minecraft.core.Direction; @@ -38,74 +32,34 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.BlockHitResult; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; -import java.util.Comparator; -import java.util.PriorityQueue; - -public class MEStockingBusPartMachine extends MEBusPartMachine { +public class MEStockingBusPartMachine extends MEBusPartMachine implements IDataStickConfigurable { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( MEStockingBusPartMachine.class, MEBusPartMachine.MANAGED_FIELD_HOLDER ); - protected final int slots; + private final int slots; + @Getter @DescSynced @Persisted - @Getter private boolean autoPull; @Getter @Setter @Persisted - @DropSaved private int minStackSize = 1; - private @UnknownNullability IStackWatcher storageWatcher; - private final GenericStackHandler configStacks; + @Persisted + protected final StockingConfigHandler configHandler; public MEStockingBusPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { super(holder, tier, IO.IN, args); this.slots = slots; - this.configStacks = new GenericStackHandler(slots) { - @Override - public void setStackInSlot(int slot, @Nullable GenericStack stack) { - GenericStack oldStack = getStackInSlot(slot); - super.setStackInSlot(slot, stack); - if (storageWatcher == null) return; - if (oldStack != null) { - storageWatcher.remove(oldStack.what()); - } - if (stack != null) { - storageWatcher.add(stack.what()); - } - } - }; - } - - @Override - protected GridNodeHost createNodeHost() { - GridNodeHost nodeHost = super.createNodeHost(); - nodeHost.getMainNode().addService(IStorageWatcherNode.class, new IStorageWatcherNode() { - @Override - public void updateWatcher(IStackWatcher newWatcher) { - storageWatcher = newWatcher; - for (int i = 0; i < configStacks.getSlots(); i++) { - GenericStack stack = configStacks.getStackInSlot(i); - if (stack != null) { - storageWatcher.add(stack.what()); - } - } - } - - @Override - public void onStackChange(AEKey what, long amount) { - getInventory().onContentsChanged(); - } - }); - return nodeHost; + this.configHandler = new StockingConfigHandler(slots, () -> getInventory().onContentsChanged()); + nodeHost.getMainNode().addService(IStorageWatcherNode.class, configHandler); } @Override @@ -126,16 +80,9 @@ public void onMainNodeStateChanged(IGridNodeListener.State reason) { } @Override - protected void updateInventorySubscription(Direction newFacing) { + protected boolean shouldUpdateSubscription(Direction newFacing) { IManagedGridNode node = nodeHost.getMainNode(); - if (isWorkingEnabled() && node.isActive() && isAutoPull()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - return; - } - if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } + return isWorkingEnabled() && node.isActive() && isAutoPull(); } @Override @@ -146,42 +93,8 @@ public void autoIO() { int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; if (getOffsetTimer() % updateInterval != 0) return; - // Refresh the configuration list in auto-pull mode. - // Sets the config to the configStacks size items with the highest amount in the ME system. KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); - - // Use a PriorityQueue to sort the stacks on size, take the first configStacks size - // biggest stacks. - var topItems = new PriorityQueue<>(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); - - for (var entry : cachedInv) { - AEKey key = entry.getKey(); - long amount = entry.getLongValue(); - - if (!(key instanceof AEItemKey)) continue; - - if (amount >= minStackSize) { - if (topItems.size() < configStacks.getSlots()) { - topItems.offer(entry); - } else if (amount > topItems.peek().getLongValue()) { - topItems.poll(); - topItems.offer(entry); - } - } - } - - // Now, topItems is a PQ with configStacks size highest amount items in the system. - for (int i = 0; i < configStacks.getSlots(); i++) { - var entry = topItems.poll(); - if (entry == null) { - configStacks.setStackInSlot(i, null); - continue; - } - AEKey what = entry.getKey(); - // Since we want our items to be displayed from highest to lowest, but poll() returns - // the lowest first, we fill in the slots starting at itemAmount-1 - configStacks.setStackInSlot(configStacks.getSlots() - i - 1, new GenericStack(what, 1)); - } + configHandler.autoPull(cachedInv, (key, amount) -> amount >= minStackSize && key instanceof AEItemKey); } @Override @@ -215,39 +128,41 @@ public void attachSideTabs(TabsWidget sideTabs) { protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, BlockHitResult hitResult) { if (!isRemote()) { setAutoPull(!autoPull); - if (autoPull) { - playerIn.sendSystemMessage( - Component.translatable("gtceu.machine.me.stocking_auto_pull_enabled")); - } else { - playerIn.sendSystemMessage( - Component.translatable("gtceu.machine.me.stocking_auto_pull_disabled")); - } + playerIn.sendSystemMessage(autoPull + ? Component.translatable("gtceu.machine.me.stocking_auto_pull_enabled") + : Component.translatable("gtceu.machine.me.stocking_auto_pull_disabled") + ); } return InteractionResult.sidedSuccess(isRemote()); } - protected CompoundTag writeConfig() { - if (!autoPull) { + @Override + public String getConfigKey() { + return MEInputBusPartMachine.CONFIG_KEY; + } - } - // if in auto-pull, no need to write actual configured slots, but still need to write the ghost circuit - CompoundTag tag = new CompoundTag(); - tag.putBoolean("AutoPull", true); - tag.putByte("GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0))); - return tag; + @Override + public Component getConfigName() { + return MEInputBusPartMachine.CONFIG_NAME; } - protected void readConfig(CompoundTag tag) { - if (tag.getBoolean("AutoPull")) { - // if being set to auto-pull, no need to read the configured slots - this.setAutoPull(true); - circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); - return; + @Override + public void writeConfig(CompoundTag tag) { + MEConfigUtil.writeAutoPull(tag, autoPull); + if (!autoPull) { + MEConfigUtil.writeConfigHandler(tag, configHandler); } - // set auto pull first to avoid issues with clearing the config after reading from the data stick - this.setAutoPull(false); + MEConfigUtil.writeGhostCircuit(tag, circuitInventory); + } + @Override + public void readConfig(CompoundTag tag) { + MEConfigUtil.readAutoPull(tag, this::setAutoPull); + if (!autoPull) { + MEConfigUtil.readConfigHandler(tag, configHandler); + getInventory().onContentsChanged(); + } + MEConfigUtil.readGhostCircuit(tag, circuitInventory); } @Override @@ -263,7 +178,7 @@ public MEStorageBackedItemHandler(int slots) { @Override public boolean isItemValid(int slot, ItemStack stack) { - AEItemKey key = getConfiguredKey(slot); + AEItemKey key = getItemKey(slot); return key != null && key.matches(stack); } @@ -271,13 +186,14 @@ public boolean isItemValid(int slot, ItemStack stack) { public ItemStack getStackInSlot(int slot) { validateSlotIndex(slot); - IGrid grid = getActiveGrid(); - if (grid == null) return ItemStack.EMPTY; + IManagedGridNode mainNode = nodeHost.getMainNode(); + if (!mainNode.isActive()) return ItemStack.EMPTY; + assert mainNode.getGrid() != null; - AEItemKey key = getConfiguredKey(slot); + AEItemKey key = getItemKey(slot); if (key == null) return ItemStack.EMPTY; - KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); + KeyCounter cachedInv = mainNode.getGrid().getStorageService().getCachedInventory(); long existing = cachedInv.get(key); return key.toStack(GTMath.saturatedCast(existing)); @@ -294,15 +210,14 @@ public ItemStack extractItem(int slot, int amount, boolean simulate) { validateSlotIndex(slot); - IGrid grid = getActiveGrid(); - if (grid == null) return ItemStack.EMPTY; + IManagedGridNode mainNode = nodeHost.getMainNode(); + if (!mainNode.isActive()) return ItemStack.EMPTY; + assert mainNode.getGrid() != null; - AEItemKey key = getConfiguredKey(slot); + AEItemKey key = getItemKey(slot); if (key == null) return ItemStack.EMPTY; - // Extract the items from the real net to either validate (simulate) - // or extract (modulate) when this is called - MEStorage networkInv = grid.getStorageService().getInventory(); + MEStorage networkInv = mainNode.getGrid().getStorageService().getInventory(); long extracted = networkInv.extract( key, amount, @@ -310,22 +225,12 @@ public ItemStack extractItem(int slot, int amount, boolean simulate) { actionSource ); + // do not call onContentsChanged() here, as it will be called by IStorageWatcherNode return key.toStack(Math.toIntExact(extracted)); } - private @Nullable AEItemKey getConfiguredKey(int slot) { - GenericStack configuredStack = configStacks.getStackInSlot(slot); - if (configuredStack == null) return null; - - assert configuredStack.what() instanceof AEItemKey; - return (AEItemKey) configuredStack.what(); - } - - private @Nullable IGrid getActiveGrid() { - IManagedGridNode gridNode = nodeHost.getMainNode(); - if (!gridNode.isActive()) return null; - - return gridNode.getGrid(); + private @Nullable AEItemKey getItemKey(int slot) { + return (AEItemKey) configHandler.getKeyInSlot(slot); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index 8f9f0301aa7..11666a0ded9 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -4,30 +4,23 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridNodeListener; import appeng.api.networking.IManagedGridNode; -import appeng.api.networking.IStackWatcher; import appeng.api.networking.storage.IStorageWatcherNode; import appeng.api.stacks.AEFluidKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; import appeng.api.stacks.KeyCounter; import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; -import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHost; -import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; +import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; +import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; import com.gregtechceu.gtceu.utils.GTMath; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.DropSaved; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import it.unimi.dsi.fastutil.objects.Object2LongMap; import lombok.Getter; import lombok.Setter; import net.minecraft.core.Direction; @@ -36,84 +29,36 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; import java.util.ArrayList; -import java.util.Comparator; -import java.util.PriorityQueue; -public class MEStockingHatchPartMachine extends MEHatchPartMachine implements IDataStickInteractable { +public class MEStockingHatchPartMachine extends MEHatchPartMachine implements IDataStickConfigurable { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( MEStockingHatchPartMachine.class, MEHatchPartMachine.MANAGED_FIELD_HOLDER ); - protected final int slots; - + @Getter @DescSynced @Persisted - @Getter private boolean autoPull; @Getter @Setter @Persisted - @DropSaved private int minStackSize = 1; - private @UnknownNullability IStackWatcher storageWatcher; - @Persisted - protected final GenericStackHandler configStacks; + protected final StockingConfigHandler configHandler; public MEStockingHatchPartMachine(IMachineBlockEntity holder, int tier, int slots, Object... args) { super(holder, tier, IO.IN, -1, slots, args); - this.slots = slots; - this.configStacks = new GenericStackHandler(slots) { - @Override - public void setStackInSlot(int slot, @Nullable GenericStack stack) { - GenericStack oldStack = getStackInSlot(slot); - super.setStackInSlot(slot, stack); - if (storageWatcher != null) { - if (oldStack != null) { - storageWatcher.remove(oldStack.what()); - } - if (stack != null) { - storageWatcher.add(stack.what()); - } - } - tank.onContentsChanged(); - } - }; - } - - @Override - protected GridNodeHost createNodeHost() { - GridNodeHost nodeHost = super.createNodeHost(); - nodeHost.getMainNode().addService(IStorageWatcherNode.class, new IStorageWatcherNode() { - @Override - public void updateWatcher(IStackWatcher newWatcher) { - storageWatcher = newWatcher; - for (int i = 0; i < configStacks.getSlots(); i++) { - GenericStack stack = configStacks.getStackInSlot(i); - if (stack != null) { - storageWatcher.add(stack.what()); - } - } - } - - @Override - public void onStackChange(AEKey what, long amount) { - tank.onContentsChanged(); - } - }); - return nodeHost; + this.configHandler = new StockingConfigHandler(slots, tank::onContentsChanged); + nodeHost.getMainNode().addService(IStorageWatcherNode.class, configHandler); } @Override @@ -132,16 +77,9 @@ public void onMainNodeStateChanged(IGridNodeListener.State reason) { } @Override - protected void updateTankSubscription(Direction newFacing) { + protected boolean shouldUpdateSubscription(Direction newFacing) { IManagedGridNode node = nodeHost.getMainNode(); - if (isWorkingEnabled() && node.isActive() && isAutoPull()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - return; - } - if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } + return isWorkingEnabled() && node.isActive() && isAutoPull(); } @Override @@ -153,32 +91,7 @@ protected void autoIO() { if (getOffsetTimer() % updateInterval != 0) return; KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); - var topFluids = new PriorityQueue<>(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); - - for (var entry : cachedInv) { - AEKey key = entry.getKey(); - long amount = entry.getLongValue(); - - if (!(key instanceof AEFluidKey)) continue; - if (amount < minStackSize) continue; - - if (topFluids.size() < configStacks.getSlots()) { - topFluids.offer(entry); - } else if (amount > topFluids.peek().getLongValue()) { - topFluids.poll(); - topFluids.offer(entry); - } - } - - for (int i = 0; i < configStacks.getSlots(); i++) { - configStacks.setStackInSlot(i, null); - } - - for (int i = 0; i < configStacks.getSlots(); i++) { - var entry = topFluids.poll(); - if (entry == null) break; - configStacks.setStackInSlot(configStacks.getSlots() - i - 1, new GenericStack(entry.getKey(), 1)); - } + configHandler.autoPull(cachedInv, (key, amount) -> amount >= minStackSize && key instanceof AEFluidKey); } public void setAutoPull(boolean autoPull) { @@ -187,75 +100,61 @@ public void setAutoPull(boolean autoPull) { } @Override - public void attachSideTabs(TabsWidget sideTabs) { - sideTabs.setMainTab(this); - } - - @Override - protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, - BlockHitResult hitResult) { + protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, BlockHitResult hitResult) { if (!isRemote()) { setAutoPull(!autoPull); - if (autoPull) { - playerIn.sendSystemMessage( - Component.translatable("gtceu.machine.me.stocking_auto_pull_enabled")); - } else { - playerIn.sendSystemMessage( - Component.translatable("gtceu.machine.me.stocking_auto_pull_disabled")); - } + playerIn.sendSystemMessage(autoPull + ? Component.translatable("gtceu.machine.me.stocking_auto_pull_enabled") + : Component.translatable("gtceu.machine.me.stocking_auto_pull_disabled") + ); } return InteractionResult.sidedSuccess(isRemote()); } @Override - public InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { - if (!isRemote()) { - CompoundTag tag = new CompoundTag(); - tag.put("MEInputHatch", writeConfig()); - dataStick.setTag(tag); - dataStick.setHoverName(Component.translatable("gtceu.machine.me.fluid_import.data_stick.name")); - player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_copy_settings")); - } - return InteractionResult.SUCCESS; + public void attachSideTabs(TabsWidget sideTabs) { + sideTabs.setMainTab(this); } +// @Override +// public void attachConfigurators(ConfiguratorPanel configuratorPanel) { +// super.attachConfigurators(configuratorPanel); +// configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( +// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), +// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), +// this::isAutoPull, +// (clickData, pressed) -> setAutoPull(pressed)) +// .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); +// configuratorPanel.attachConfigurators(new AutoStockingFancyConfigurator(this)); +// } + @Override - public InteractionResult onDataStickUse(Player player, ItemStack dataStick) { - CompoundTag tag = dataStick.getTag(); - if (tag == null || !tag.contains("MEInputHatch")) { - return InteractionResult.PASS; - } - if (!isRemote()) { - readConfig(tag.getCompound("MEInputHatch")); - player.sendSystemMessage(Component.translatable("gtceu.machine.me.import_paste_settings")); - } - return InteractionResult.sidedSuccess(isRemote()); + public String getConfigKey() { + return MEInputHatchPartMachine.CONFIG_KEY; + } + + @Override + public Component getConfigName() { + return MEInputHatchPartMachine.CONFIG_NAME; } - protected CompoundTag writeConfig() { - CompoundTag tag = new CompoundTag(); - tag.putBoolean("AutoPull", autoPull); + @Override + public void writeConfig(CompoundTag tag) { + MEConfigUtil.writeAutoPull(tag, autoPull); if (!autoPull) { - tag.put("ConfigStacks", configStacks.serializeNBT()); + MEConfigUtil.writeConfigHandler(tag, configHandler); } - tag.putByte( - "GhostCircuit", - (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) - ); - return tag; + MEConfigUtil.writeGhostCircuit(tag, circuitInventory); } - protected void readConfig(CompoundTag tag) { - setAutoPull(tag.getBoolean("AutoPull")); - if (!autoPull && tag.contains("ConfigStacks")) { - var oldStacks = captureConfiguredKeys(); - configStacks.deserializeNBT(tag.getCompound("ConfigStacks")); - syncStorageWatcher(oldStacks); + @Override + public void readConfig(CompoundTag tag) { + MEConfigUtil.readAutoPull(tag, this::setAutoPull); + if (!autoPull) { + MEConfigUtil.readConfigHandler(tag, configHandler); tank.onContentsChanged(); } - if (tag.contains("GhostCircuit")) { - circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte("GhostCircuit"))); - } + MEConfigUtil.readGhostCircuit(tag, circuitInventory); } @Override @@ -263,44 +162,6 @@ public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - private @Nullable AEFluidKey getConfiguredKey(int slot) { - GenericStack configuredStack = configStacks.getStackInSlot(slot); - if (configuredStack == null) return null; - - assert configuredStack.what() instanceof AEFluidKey; - return (AEFluidKey) configuredStack.what(); - } - - private @Nullable IGrid getActiveGrid() { - IManagedGridNode gridNode = nodeHost.getMainNode(); - if (!gridNode.isActive()) return null; - return gridNode.getGrid(); - } - - private GenericStack[] captureConfiguredKeys() { - GenericStack[] stacks = new GenericStack[configStacks.getSlots()]; - for (int i = 0; i < stacks.length; i++) { - stacks[i] = configStacks.getStackInSlot(i); - } - return stacks; - } - - private void syncStorageWatcher(GenericStack[] oldStacks) { - if (storageWatcher == null) return; - - for (GenericStack stack : oldStacks) { - if (stack != null) { - storageWatcher.remove(stack.what()); - } - } - for (int i = 0; i < configStacks.getSlots(); i++) { - GenericStack stack = configStacks.getStackInSlot(i); - if (stack != null) { - storageWatcher.add(stack.what()); - } - } - } - private final class MEStorageBackedFluidStorage extends CustomFluidTank { private final int slot; @@ -317,13 +178,14 @@ public void setFluid(FluidStack stack) { @Override public FluidStack getFluid() { - IGrid grid = getActiveGrid(); - if (grid == null) return FluidStack.EMPTY; + IManagedGridNode mainNode = nodeHost.getMainNode(); + if (!mainNode.isActive()) return FluidStack.EMPTY; + assert mainNode.getGrid() != null; - AEFluidKey key = getConfiguredKey(slot); + AEFluidKey key = getFluidKey(slot); if (key == null) return FluidStack.EMPTY; - long existing = grid.getStorageService().getCachedInventory().get(key); + long existing = mainNode.getGrid().getStorageService().getCachedInventory().get(key); if (existing <= 0) return FluidStack.EMPTY; return key.toStack(GTMath.saturatedCast(existing)); @@ -331,25 +193,25 @@ public FluidStack getFluid() { @Override public boolean isFluidValid(FluidStack stack) { - AEFluidKey key = getConfiguredKey(slot); + AEFluidKey key = getFluidKey(slot); return key != null && key.matches(stack); } @Override - public int fill(FluidStack resource, FluidAction action) { - return 0; + public boolean supportsFill(int tank) { + return false; } @Override - public boolean supportsFill(int tank) { - return false; + public int fill(FluidStack resource, FluidAction action) { + return 0; } @Override public FluidStack drain(FluidStack resource, FluidAction action) { if (resource.isEmpty()) return FluidStack.EMPTY; - AEFluidKey key = getConfiguredKey(slot); + AEFluidKey key = getFluidKey(slot); if (key == null || !key.matches(resource)) return FluidStack.EMPTY; return extract(key, resource.getAmount(), action); @@ -359,17 +221,18 @@ public FluidStack drain(FluidStack resource, FluidAction action) { public FluidStack drain(int maxDrain, FluidAction action) { if (maxDrain <= 0) return FluidStack.EMPTY; - AEFluidKey key = getConfiguredKey(slot); + AEFluidKey key = getFluidKey(slot); if (key == null) return FluidStack.EMPTY; return extract(key, maxDrain, action); } private FluidStack extract(AEFluidKey key, int amount, FluidAction action) { - IGrid grid = getActiveGrid(); - if (grid == null) return FluidStack.EMPTY; + IManagedGridNode mainNode = nodeHost.getMainNode(); + if (!mainNode.isActive()) return FluidStack.EMPTY; + assert mainNode.getGrid() != null; - MEStorage networkInv = grid.getStorageService().getInventory(); + MEStorage networkInv = mainNode.getGrid().getStorageService().getInventory(); long extracted = networkInv.extract( key, amount, @@ -378,10 +241,12 @@ private FluidStack extract(AEFluidKey key, int amount, FluidAction action) { ); if (extracted <= 0) return FluidStack.EMPTY; - if (action.execute()) { - onContentsChanged(); - } + // do not call onContentsChanged() here, as it will be called by IStorageWatcherNode return key.toStack(Math.toIntExact(extracted)); } + + private @Nullable AEFluidKey getFluidKey(int slot) { + return (AEFluidKey) configHandler.getKeyInSlot(slot); + } } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java new file mode 100644 index 00000000000..6ff71705490 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java @@ -0,0 +1,60 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import com.gregtechceu.gtceu.common.item.IntCircuitBehaviour; +import lombok.experimental.UtilityClass; +import net.minecraft.nbt.CompoundTag; +import net.minecraftforge.items.IItemHandlerModifiable; + +import java.util.function.Consumer; + +@UtilityClass +public class MEConfigUtil { + + private final String TAG_CONFIG_HANDLER = "ConfigHandler"; + private final String TAG_GHOST_CIRCUIT = "GhostCircuit"; + private final String TAG_AUTO_PULL = "AutoPull"; + private final String TAG_DISTINCT_BUSES = "DistinctBuses"; + + public void writeConfigHandler(CompoundTag tag, GenericStackHandler configHandler) { + tag.put(TAG_CONFIG_HANDLER, configHandler.serializeNBT()); + } + + public void readConfigHandler(CompoundTag tag, GenericStackHandler configHandler) { + if (tag.contains(TAG_CONFIG_HANDLER)) { + configHandler.deserializeNBT(tag.getCompound(TAG_CONFIG_HANDLER)); + } + } + + public void writeGhostCircuit(CompoundTag tag, IItemHandlerModifiable circuitInventory) { + tag.putByte( + TAG_GHOST_CIRCUIT, + (byte) IntCircuitBehaviour.getCircuitConfiguration(circuitInventory.getStackInSlot(0)) + ); + } + + public void readGhostCircuit(CompoundTag tag, IItemHandlerModifiable circuitInventory) { + if (tag.contains(TAG_GHOST_CIRCUIT)) { + circuitInventory.setStackInSlot(0, IntCircuitBehaviour.stack(tag.getByte(TAG_GHOST_CIRCUIT))); + } + } + + public void writeAutoPull(CompoundTag tag, boolean autoPull) { + tag.putBoolean(TAG_AUTO_PULL, autoPull); + } + + public void readAutoPull(CompoundTag tag, Consumer setter) { + if (tag.contains(TAG_AUTO_PULL)) { + setter.accept(false); + } + } + + public void writeDistinctBuses(CompoundTag tag, boolean distinctBuses) { + tag.putBoolean(TAG_DISTINCT_BUSES, distinctBuses); + } + + public void readDistinctBuses(CompoundTag tag, Consumer setter) { + if (tag.contains(TAG_DISTINCT_BUSES)) { + setter.accept(tag.getBoolean(TAG_DISTINCT_BUSES)); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java new file mode 100644 index 00000000000..30a0f3e5144 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java @@ -0,0 +1,120 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import appeng.api.networking.IStackWatcher; +import appeng.api.networking.storage.IStorageWatcherNode; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.api.stacks.KeyCounter; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import net.minecraft.nbt.CompoundTag; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.util.Comparator; +import java.util.Objects; +import java.util.PriorityQueue; + +public class StockingConfigHandler extends GenericStackHandler implements IStorageWatcherNode { + + private @UnknownNullability IStackWatcher storageWatcher; + private final Runnable changeListener; + + public StockingConfigHandler(int size, Runnable changeListener) { + super(size); + this.changeListener = changeListener; + } + + @Override + public void updateWatcher(IStackWatcher newWatcher) { + storageWatcher = newWatcher; + syncWatcher(); + } + + @Override + public void onStackChange(AEKey what, long amount) { + changeListener.run(); + } + + @Override + public void setStackInSlot(int slot, @Nullable GenericStack newStack) { + AEKey oldKey = stacks[slot] == null ? null : stacks[slot].what(); + AEKey newKey = newStack == null ? null : newStack.what(); + + if (Objects.equals(oldKey, newKey)) return; + + super.setStackInSlot(slot, newStack); + + // Notify the listener that the configuration has been updated + changeListener.run(); + + if (storageWatcher == null) { + return; + } + + // Remove watcher entry for the previous stack + if (oldKey != null) { + storageWatcher.remove(oldKey); + } + + // Register watcher entry for the new stack + if (newKey != null) { + storageWatcher.add(newKey); + } + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + super.deserializeNBT(nbt); + syncWatcher(); + } + + /** + * Auto-refresh the configuration list by selecting the keys with the highest amounts from the ME system. + * Only keys passing the provided filter are considered. + * + * @param source The ME system to pull keys from + * @param filter A predicate to filter which keys should be included + */ + public void autoPull(KeyCounter source, StackFilter filter) { + int slots = getSlots(); + if (slots == 0) return; + + // Top K algorithm: Use a Min-heap to keep the top 'slotCount' entries with the highest amount + var topEntries = new PriorityQueue<>(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); + + for (var entry : source) { + AEKey key = entry.getKey(); + long amount = entry.getLongValue(); + + if (!filter.test(key, amount)) { + continue; + } + + topEntries.offer(entry); + if (topEntries.size() > slots) { + topEntries.poll(); + } + } + + // Fill configuration slots from highest to lowest amount + for (int i = slots - 1; !topEntries.isEmpty(); i--) { + var entry = topEntries.poll(); + setKeyInSlot(i, entry.getKey()); + } + } + + private void syncWatcher() { + if (storageWatcher == null) return; + + storageWatcher.reset(); + for (GenericStack stack : stacks) { + if (stack != null) { + storageWatcher.add(stack.what()); + } + } + } + + public interface StackFilter { + boolean test(AEKey key, long amount); + } +} From dc35c2414c485e5fbea162a83a928c8bc1202570 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Thu, 26 Mar 2026 00:13:43 +0800 Subject: [PATCH 09/21] fixed ME part machine constructors --- .../gtceu/common/data/machines/GTAEMachines.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java index 72abe4159a2..35a39406d97 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java @@ -16,7 +16,7 @@ public class GTAEMachines { public final static MachineDefinition ITEM_IMPORT_BUS_ME = REGISTRATE - .machine("me_input_bus", MEInputBusPartMachine::new) + .machine("me_input_bus", be -> new MEInputBusPartMachine(be, EV, 16)) .langValue("ME Input Bus") .tier(EV) .rotationState(RotationState.ALL) @@ -30,7 +30,7 @@ public class GTAEMachines { .register(); public final static MachineDefinition STOCKING_IMPORT_BUS_ME = REGISTRATE - .machine("me_stocking_input_bus", MEStockingBusPartMachine::new) + .machine("me_stocking_input_bus", be -> new MEStockingBusPartMachine(be, LuV, 16)) .langValue("ME Stocking Input Bus") .tier(LuV) .rotationState(RotationState.ALL) @@ -60,7 +60,7 @@ public class GTAEMachines { .register(); public final static MachineDefinition FLUID_IMPORT_HATCH_ME = REGISTRATE - .machine("me_input_hatch", MEInputHatchPartMachine::new) + .machine("me_input_hatch", be -> new MEInputHatchPartMachine(be, EV, 16)) .langValue("ME Input Hatch") .tier(EV) .rotationState(RotationState.ALL) @@ -74,7 +74,7 @@ public class GTAEMachines { .register(); public final static MachineDefinition STOCKING_IMPORT_HATCH_ME = REGISTRATE - .machine("me_stocking_input_hatch", MEStockingHatchPartMachine::new) + .machine("me_stocking_input_hatch", be -> new MEStockingHatchPartMachine(be, LuV, 16)) .langValue("ME Stocking Input Hatch") .tier(LuV) .rotationState(RotationState.ALL) @@ -118,7 +118,7 @@ public class GTAEMachines { Component.translatable("gtceu.part_sharing.enabled")) .register(); public static final MachineDefinition ME_PATTERN_BUFFER_PROXY = REGISTRATE - .machine("me_pattern_buffer_proxy", MEPatternBufferProxyPartMachine::new) + .machine("me_pattern_buffer_proxy", MEPatternProxyPartMachine::new) .tier(LuV) .rotationState(RotationState.ALL) .abilities(PartAbility.IMPORT_ITEMS, PartAbility.IMPORT_FLUIDS, PartAbility.EXPORT_FLUIDS, From 3e5af28a9e5801392ab0e69fdf604046c58e4744 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Thu, 26 Mar 2026 02:01:53 +0800 Subject: [PATCH 10/21] refactor stocking fancy configurator to generic class --- .../AutoStockingFancyConfigurator.java | 53 ----------------- .../gtceu/data/lang/MachineLang.java | 4 -- .../StockingFancyConfigurator.java | 58 +++++++++++++++++++ .../ae2/machine/MEStockingBusPartMachine.java | 33 +++++++---- .../machine/MEStockingHatchPartMachine.java | 32 ++++++---- 5 files changed, 101 insertions(+), 79 deletions(-) delete mode 100644 src/main/java/com/gregtechceu/gtceu/api/machine/fancyconfigurator/AutoStockingFancyConfigurator.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/fancyconfigurator/StockingFancyConfigurator.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/fancyconfigurator/AutoStockingFancyConfigurator.java b/src/main/java/com/gregtechceu/gtceu/api/machine/fancyconfigurator/AutoStockingFancyConfigurator.java deleted file mode 100644 index 94620e8736c..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/fancyconfigurator/AutoStockingFancyConfigurator.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gregtechceu.gtceu.api.machine.fancyconfigurator; - -import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfigurator; -import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; -import com.gregtechceu.gtceu.common.data.GTItems; -import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.integration.ae2.machine.MEStockingBusPartMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.multiblock.IMEStockingPart; - -import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; -import com.lowdragmc.lowdraglib.gui.texture.ItemStackTexture; -import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; -import com.lowdragmc.lowdraglib.gui.widget.Widget; -import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; - -import net.minecraft.network.chat.Component; - -public class AutoStockingFancyConfigurator implements IFancyConfigurator { - - private IMEStockingPart machine; - - public AutoStockingFancyConfigurator(IMEStockingPart machine) { - this.machine = machine; - } - - @Override - public Component getTitle() { - return Component.translatable("gtceu.gui.adv_stocking_config.title"); - } - - @Override - public IGuiTexture getIcon() { - return new ItemStackTexture(GTItems.TOOL_DATA_STICK.asStack()); - } - - @Override - public Widget createConfigurator() { - var group = new WidgetGroup(0, 0, 90, 70); - - String suffix = machine instanceof MEStockingBusPartMachine ? "min_item_count" : "min_fluid_count"; - - group.addWidget(new LabelWidget(4, 2, "gtceu.gui.title.adv_stocking_config." + suffix)); - group.addWidget(new IntInputWidget(4, 12, 81, 14, machine::getMinStackSize, - machine::setMinStackSize).setMin(1) - .appendHoverTooltips(Component.translatable("gtceu.gui.adv_stocking_config." + suffix))); - group.addWidget(new LabelWidget(4, 36, "gtceu.gui.title.adv_stocking_config.ticks_per_cycle")); - group.addWidget(new IntInputWidget(4, 46, 81, 14, machine::getTicksPerCycle, - machine::setTicksPerCycle).setMin(ConfigHolder.INSTANCE.compat.ae2.updateIntervals) - .setHoverTooltips(Component.translatable("gtceu.gui.adv_stocking_config.ticks_per_cycle"))); - - return group; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/MachineLang.java b/src/main/java/com/gregtechceu/gtceu/data/lang/MachineLang.java index 0efc336ac21..05a5cfa75a0 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/MachineLang.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/MachineLang.java @@ -810,10 +810,6 @@ protected static void init(RegistrateLangProvider provider) { "Minimum Item Stack Size for Automated Pulling"); provider.add("gtceu.gui.adv_stocking_config.min_fluid_count", "Minimum Fluid Stack Size for Automated Pulling"); - provider.add("gtceu.gui.title.adv_stocking_config.ticks_per_cycle", - "Ticks Per Cycle"); - provider.add("gtceu.gui.adv_stocking_config.ticks_per_cycle", - "Delay between item list updates"); provider.add("gtceu.gui.adv_stocking_config.title", "Configure Automatic Stocking"); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/fancyconfigurator/StockingFancyConfigurator.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/fancyconfigurator/StockingFancyConfigurator.java new file mode 100644 index 00000000000..edafb8a72f2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/fancyconfigurator/StockingFancyConfigurator.java @@ -0,0 +1,58 @@ +package com.gregtechceu.gtceu.integration.ae2.gui.fancyconfigurator; + +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfigurator; +import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.common.data.GTItems; + +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; +import com.lowdragmc.lowdraglib.gui.texture.ItemStackTexture; +import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; + +import net.minecraft.network.chat.Component; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class StockingFancyConfigurator implements IFancyConfigurator { + + private final String minSizeLabel; + private final String minSizeTooltip; + private final Supplier minSizeGetter; + private final Consumer minSizeSetter; + + public StockingFancyConfigurator( + String minSizeLabel, + String minSizeTooltip, + Supplier minSizeGetter, + Consumer minSizeSetter + ) { + this.minSizeLabel = minSizeLabel; + this.minSizeTooltip = minSizeTooltip; + this.minSizeGetter = minSizeGetter; + this.minSizeSetter = minSizeSetter; + } + + @Override + public Component getTitle() { + return Component.translatable("gtceu.gui.adv_stocking_config.title"); + } + + @Override + public IGuiTexture getIcon() { + return new ItemStackTexture(GTItems.TOOL_DATA_STICK.asStack()); + } + + @Override + public Widget createConfigurator() { + var group = new WidgetGroup(0, 0, 90, 30); + + group.addWidget(new LabelWidget(4, 2, minSizeLabel)); + group.addWidget(new IntInputWidget(4, 12, 81, 14, minSizeGetter, minSizeSetter) + .setMin(1) + .appendHoverTooltips(Component.translatable(minSizeTooltip))); + + return group; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index 4774cc3832b..54b5f307991 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -9,12 +9,16 @@ import appeng.api.stacks.KeyCounter; import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.integration.ae2.gui.fancyconfigurator.StockingFancyConfigurator; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; import com.gregtechceu.gtceu.utils.GTMath; @@ -33,6 +37,8 @@ import net.minecraft.world.phys.BlockHitResult; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class MEStockingBusPartMachine extends MEBusPartMachine implements IDataStickConfigurable { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( @@ -112,17 +118,22 @@ public void attachSideTabs(TabsWidget sideTabs) { sideTabs.setMainTab(this); // removes the cover configurator, it's pointless and clashes with layout. } -// @Override -// public void attachConfigurators(ConfiguratorPanel configuratorPanel) { -// super.attachConfigurators(configuratorPanel); -// configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( -// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), -// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), -// this::isAutoPull, -// (clickData, pressed) -> setAutoPull(pressed)) -// .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); -// configuratorPanel.attachConfigurators(new AutoStockingFancyConfigurator(this)); -// } + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + super.attachConfigurators(configuratorPanel); + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), + GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), + this::isAutoPull, + (clickData, pressed) -> setAutoPull(pressed)) + .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); + configuratorPanel.attachConfigurators(new StockingFancyConfigurator( + "gtceu.gui.title.adv_stocking_config.min_item_count", + "gtceu.gui.adv_stocking_config.min_item_count", + this::getMinStackSize, + this::setMinStackSize) + ); + } @Override protected InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, Direction gridSide, BlockHitResult hitResult) { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index 11666a0ded9..6b482bcca8f 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -9,12 +9,16 @@ import appeng.api.stacks.KeyCounter; import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.fancy.ConfiguratorPanel; +import com.gregtechceu.gtceu.api.gui.fancy.IFancyConfiguratorButton; import com.gregtechceu.gtceu.api.gui.fancy.TabsWidget; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.integration.ae2.gui.fancyconfigurator.StockingFancyConfigurator; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; import com.gregtechceu.gtceu.utils.GTMath; @@ -34,6 +38,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.List; public class MEStockingHatchPartMachine extends MEHatchPartMachine implements IDataStickConfigurable { @@ -116,17 +121,22 @@ public void attachSideTabs(TabsWidget sideTabs) { sideTabs.setMainTab(this); } -// @Override -// public void attachConfigurators(ConfiguratorPanel configuratorPanel) { -// super.attachConfigurators(configuratorPanel); -// configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( -// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), -// GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), -// this::isAutoPull, -// (clickData, pressed) -> setAutoPull(pressed)) -// .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); -// configuratorPanel.attachConfigurators(new AutoStockingFancyConfigurator(this)); -// } + @Override + public void attachConfigurators(ConfiguratorPanel configuratorPanel) { + super.attachConfigurators(configuratorPanel); + configuratorPanel.attachConfigurators(new IFancyConfiguratorButton.Toggle( + GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0, 1, 0.5), + GuiTextures.BUTTON_AUTO_PULL.getSubTexture(0, 0.5, 1, 0.5), + this::isAutoPull, + (clickData, pressed) -> setAutoPull(pressed)) + .setTooltipsSupplier(pressed -> List.of(Component.translatable("gtceu.gui.me_bus.auto_pull_button")))); + configuratorPanel.attachConfigurators(new StockingFancyConfigurator( + "gtceu.gui.title.adv_stocking_config.min_fluid_count", + "gtceu.gui.adv_stocking_config.min_fluid_count", + this::getMinStackSize, + this::setMinStackSize + )); + } @Override public String getConfigKey() { From 7cdae29f194a6b750d734fd96b28da5aa16a0571 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Thu, 26 Mar 2026 02:46:58 +0800 Subject: [PATCH 11/21] reorganize packages --- .../ae2/gui/AEFluidConfigWidget.java | 39 +++ .../ae2/gui/AEItemConfigWidget.java | 44 +++ .../{widget => }/AETextInputButtonWidget.java | 2 +- .../ae2/gui/{widget => }/AmountSetWidget.java | 8 +- .../ae2/gui/{widget => }/ConfigWidget.java | 26 +- .../list/AEFluidDisplayWidget.java | 4 +- .../list/AEItemDisplayWidget.java | 4 +- .../{widget => }/list/AEListGridWidget.java | 16 +- .../{widget => }/slot/AEConfigSlotWidget.java | 8 +- .../slot/AEFluidConfigSlotWidget.java | 28 +- .../slot/AEItemConfigSlotWidget.java | 19 +- .../slot/AEPatternViewSlotWidget.java | 2 +- .../ae2/gui/widget/AEFluidConfigWidget.java | 39 --- .../ae2/gui/widget/AEItemConfigWidget.java | 39 --- .../ae2/machine/MEOutputBusPartMachine.java | 296 +++++++++++------- .../ae2/machine/MEOutputHatchPartMachine.java | 141 +++++---- .../machine/MEPatternBufferPartMachine.java | 34 +- ...ne.java => MEPatternProxyPartMachine.java} | 11 +- .../ae2/machine/MEStockingBusPartMachine.java | 2 +- .../integration/ae2/utils/AEKeyStorage.java | 136 ++++++++ .../ae2/utils/DynamicItemStackHandler.java | 173 ++++++++++ .../InternalSlotRecipeHandler.java | 2 +- .../integration/ae2/utils/KeyStorage.java | 102 ------ .../ProxySlotRecipeHandler.java | 10 +- .../jade/provider/GTFluidStorageProvider.java | 4 +- .../jade/provider/GTItemStorageProvider.java | 4 +- .../MEPatternBufferProxyProvider.java | 6 +- 27 files changed, 741 insertions(+), 458 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEFluidConfigWidget.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEItemConfigWidget.java rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/AETextInputButtonWidget.java (97%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/AmountSetWidget.java (90%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/ConfigWidget.java (87%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/list/AEFluidDisplayWidget.java (95%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/list/AEItemDisplayWidget.java (93%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/list/AEListGridWidget.java (93%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/slot/AEConfigSlotWidget.java (93%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/slot/AEFluidConfigSlotWidget.java (92%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/slot/AEItemConfigSlotWidget.java (93%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/{widget => }/slot/AEPatternViewSlotWidget.java (98%) delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEFluidConfigWidget.java delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEItemConfigWidget.java rename src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/{MEPatternBufferProxyPartMachine.java => MEPatternProxyPartMachine.java} (90%) create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/DynamicItemStackHandler.java rename src/main/java/com/gregtechceu/gtceu/integration/ae2/{machine/trait => utils}/InternalSlotRecipeHandler.java (98%) delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/KeyStorage.java rename src/main/java/com/gregtechceu/gtceu/integration/ae2/{machine/trait => utils}/ProxySlotRecipeHandler.java (94%) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEFluidConfigWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEFluidConfigWidget.java new file mode 100644 index 00000000000..9564013ddf6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEFluidConfigWidget.java @@ -0,0 +1,39 @@ +package com.gregtechceu.gtceu.integration.ae2.gui; + +import com.gregtechceu.gtceu.integration.ae2.gui.slot.AEFluidConfigSlotWidget; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableFluidList; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableFluidSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; + +import appeng.api.stacks.GenericStack; + +public class AEFluidConfigWidget extends ConfigWidget { + + private final ConfigurableFluidList fluidList; + + public AEFluidConfigWidget(int x, int y, ConfigurableFluidList list) { + super(x, y, list.getInventory(), list.isStocking()); + this.fluidList = list; + } + + @Override + void init() { + int line; + this.displayList = new ConfigurableSlot[this.config.length]; + this.cached = new ConfigurableSlot[this.config.length]; + for (int index = 0; index < this.config.length; index++) { + this.displayList[index] = new ConfigurableFluidSlot(); + this.cached[index] = new ConfigurableFluidSlot(); + line = index / 8; + this.addWidget(new AEFluidConfigSlotWidget((index - line * 8) * 18, line * (18 * 2 + 2), this, index)); + } + } + + public boolean hasStackInConfig(GenericStack stack) { + return fluidList.isStackConfigured(stack, true); + } + + public boolean isAutoPull() { + return fluidList.isAutoPull(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEItemConfigWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEItemConfigWidget.java new file mode 100644 index 00000000000..d905eacb0a4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AEItemConfigWidget.java @@ -0,0 +1,44 @@ +package com.gregtechceu.gtceu.integration.ae2.gui; + +import com.gregtechceu.gtceu.integration.ae2.gui.slot.AEItemConfigSlotWidget; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableItemList; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableItemSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; + +import appeng.api.stacks.GenericStack; + +public class AEItemConfigWidget extends ConfigWidget { + + private static final int SLOTS_PER_ROW = 8; + private static final int SLOT_SIZE = 18; + private static final int ROW_SPACING = 2; + + private final ConfigurableItemList itemList; + + public AEItemConfigWidget(int x, int y, ConfigurableItemList list) { + super(x, y, list.getInventory(), list.isStocking()); + this.itemList = list; + } + + @Override + void init() { + int line; + this.displayList = new ConfigurableSlot[this.config.length]; + this.cached = new ConfigurableSlot[this.config.length]; + for (int index = 0; index < this.config.length; index++) { + this.displayList[index] = new ConfigurableItemSlot(); + this.cached[index] = new ConfigurableItemSlot(); + line = index / SLOTS_PER_ROW; + this.addWidget(new AEItemConfigSlotWidget((index - line * SLOTS_PER_ROW) * SLOT_SIZE, + line * (SLOT_SIZE * 2 + ROW_SPACING), this, index)); + } + } + + public boolean hasStackInConfig(GenericStack stack) { + return itemList.isStackConfigured(stack, true); + } + + public boolean isAutoPull() { + return itemList.isAutoPull(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AETextInputButtonWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AETextInputButtonWidget.java similarity index 97% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AETextInputButtonWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AETextInputButtonWidget.java index 2baeffb5d8b..8cc537fa304 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AETextInputButtonWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AETextInputButtonWidget.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget; +package com.gregtechceu.gtceu.integration.ae2.gui; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AmountSetWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AmountSetWidget.java similarity index 90% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AmountSetWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AmountSetWidget.java index 1139bd36c2f..41097f275e6 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AmountSetWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/AmountSetWidget.java @@ -1,7 +1,7 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget; +package com.gregtechceu.gtceu.integration.ae2.gui; import com.gregtechceu.gtceu.api.gui.GuiTextures; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; import com.lowdragmc.lowdraglib.gui.widget.TextFieldWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; @@ -47,7 +47,7 @@ public String getAmountStr() { if (this.index < 0) { return "0"; } - IConfigurableSlot slot = this.parentWidget.getConfig(this.index); + ConfigurableSlot slot = this.parentWidget.getConfig(this.index); if (slot.getConfig() != null) { return String.valueOf(slot.getConfig().amount()); } @@ -60,7 +60,7 @@ public void setNewAmount(String amount) { if (this.index < 0) { return; } - IConfigurableSlot slot = this.parentWidget.getConfig(this.index); + ConfigurableSlot slot = this.parentWidget.getConfig(this.index); if (newAmount > 0 && slot.getConfig() != null) { slot.setConfig(new GenericStack(slot.getConfig().what(), newAmount)); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/ConfigWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/ConfigWidget.java similarity index 87% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/ConfigWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/ConfigWidget.java index 69198688a73..4c0f2592584 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/ConfigWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/ConfigWidget.java @@ -1,7 +1,7 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget; +package com.gregtechceu.gtceu.integration.ae2.gui; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEConfigSlotWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; +import com.gregtechceu.gtceu.integration.ae2.gui.slot.AEConfigSlotWidget; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -19,17 +19,17 @@ public abstract class ConfigWidget extends WidgetGroup { - protected final IConfigurableSlot[] config; - protected IConfigurableSlot[] cached; - protected Int2ObjectMap changeMap = new Int2ObjectOpenHashMap<>(); - protected IConfigurableSlot[] displayList; + protected final ConfigurableSlot[] config; + protected ConfigurableSlot[] cached; + protected Int2ObjectMap changeMap = new Int2ObjectOpenHashMap<>(); + protected ConfigurableSlot[] displayList; protected AmountSetWidget amountSetWidget; protected final static int UPDATE_ID = 1000; @Getter protected final boolean isStocking; - public ConfigWidget(int x, int y, IConfigurableSlot[] config, boolean isStocking) { + public ConfigWidget(int x, int y, ConfigurableSlot[] config, boolean isStocking) { super(new Position(x, y), new Size(config.length / 2 * 18, 18 * 4 + 2)); this.isStocking = isStocking; this.config = config; @@ -95,8 +95,8 @@ public void detectAndSendChanges() { super.detectAndSendChanges(); this.changeMap.clear(); for (int index = 0; index < this.config.length; index++) { - IConfigurableSlot newSlot = this.config[index]; - IConfigurableSlot oldSlot = this.cached[index]; + ConfigurableSlot newSlot = this.config[index]; + ConfigurableSlot oldSlot = this.cached[index]; GenericStack nConfig = newSlot.getConfig(); GenericStack nStock = newSlot.getStock(); GenericStack oConfig = oldSlot.getConfig(); @@ -139,7 +139,7 @@ public void readUpdateInfo(int id, FriendlyByteBuf buffer) { int size = buffer.readVarInt(); for (int i = 0; i < size; i++) { int index = buffer.readVarInt(); - IConfigurableSlot slot = this.displayList[index]; + ConfigurableSlot slot = this.displayList[index]; if (buffer.readBoolean()) { slot.setConfig(GenericStack.readBuffer(buffer)); } else { @@ -154,11 +154,11 @@ public void readUpdateInfo(int id, FriendlyByteBuf buffer) { } } - public final IConfigurableSlot getConfig(int index) { + public final ConfigurableSlot getConfig(int index) { return this.config[index]; } - public final IConfigurableSlot getDisplay(int index) { + public final ConfigurableSlot getDisplay(int index) { return this.displayList[index]; } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEFluidDisplayWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEFluidDisplayWidget.java similarity index 95% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEFluidDisplayWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEFluidDisplayWidget.java index 08dd66ce998..c65e6443810 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEFluidDisplayWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEFluidDisplayWidget.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.list; +package com.gregtechceu.gtceu.integration.ae2.gui.list; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.client.TooltipsHandler; @@ -24,7 +24,7 @@ import java.util.List; import java.util.Optional; -import static com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEConfigSlotWidget.drawSelectionOverlay; +import static com.gregtechceu.gtceu.integration.ae2.gui.slot.AEConfigSlotWidget.drawSelectionOverlay; import static com.lowdragmc.lowdraglib.gui.util.DrawerHelper.drawText; /** diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEItemDisplayWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEItemDisplayWidget.java similarity index 93% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEItemDisplayWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEItemDisplayWidget.java index 3c28a821248..d6b1d6d2231 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEItemDisplayWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEItemDisplayWidget.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.list; +package com.gregtechceu.gtceu.integration.ae2.gui.list; import com.gregtechceu.gtceu.api.gui.GuiTextures; @@ -14,7 +14,7 @@ import appeng.api.stacks.GenericStack; import org.jetbrains.annotations.NotNull; -import static com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEConfigSlotWidget.drawSelectionOverlay; +import static com.gregtechceu.gtceu.integration.ae2.gui.slot.AEConfigSlotWidget.drawSelectionOverlay; import static com.lowdragmc.lowdraglib.gui.util.DrawerHelper.drawItemStack; import static com.lowdragmc.lowdraglib.gui.util.DrawerHelper.drawText; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEListGridWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEListGridWidget.java similarity index 93% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEListGridWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEListGridWidget.java index fe52076fd63..ff2c9c480f7 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/list/AEListGridWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/list/AEListGridWidget.java @@ -1,6 +1,6 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.list; +package com.gregtechceu.gtceu.integration.ae2.gui.list; -import com.gregtechceu.gtceu.integration.ae2.utils.KeyStorage; +import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; import com.lowdragmc.lowdraglib.gui.widget.DraggableScrollableWidgetGroup; import com.lowdragmc.lowdraglib.gui.widget.Widget; @@ -20,21 +20,21 @@ import java.util.List; /** - * A display only widget for {@link KeyStorage} + * A display only widget for {@link AEKeyStorage} */ public abstract class AEListGridWidget extends DraggableScrollableWidgetGroup { - protected final KeyStorage list; + protected final AEKeyStorage list; private final int slotAmountY; private int slotRowsAmount; protected final static int ROW_CHANGE_ID = 2; protected final static int CONTENT_CHANGE_ID = 3; protected final Object2LongMap changeMap = new Object2LongOpenHashMap<>(); - protected final KeyStorage cached = new KeyStorage(); + protected final AEKeyStorage cached = new AEKeyStorage(); protected final List displayList = new ArrayList<>(); - public AEListGridWidget(int x, int y, int slotsY, KeyStorage internalList) { + public AEListGridWidget(int x, int y, int slotsY, AEKeyStorage internalList) { super(x, y, 18 + 140, slotsY * 18); this.list = internalList; this.slotAmountY = slotsY; @@ -189,7 +189,7 @@ public void readInitialData(FriendlyByteBuf buffer) { public static class Item extends AEListGridWidget { - public Item(int x, int y, int slotsY, KeyStorage internalList) { + public Item(int x, int y, int slotsY, AEKeyStorage internalList) { super(x, y, slotsY, internalList); } @@ -211,7 +211,7 @@ protected Widget createDisplayWidget(int x, int y, int index) { public static class Fluid extends AEListGridWidget { - public Fluid(int x, int y, int slotsY, KeyStorage internalList) { + public Fluid(int x, int y, int slotsY, AEKeyStorage internalList) { super(x, y, slotsY, internalList); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEConfigSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEConfigSlotWidget.java similarity index 93% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEConfigSlotWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEConfigSlotWidget.java index b6d04d492ce..9c412638333 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEConfigSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEConfigSlotWidget.java @@ -1,7 +1,7 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.slot; +package com.gregtechceu.gtceu.integration.ae2.gui.slot; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.ConfigWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; +import com.gregtechceu.gtceu.integration.ae2.gui.ConfigWidget; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.utils.Position; @@ -43,7 +43,7 @@ public AEConfigSlotWidget(Position pos, Size size, ConfigWidget widget, int inde @Override public void drawInForeground(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.drawInForeground(graphics, mouseX, mouseY, partialTicks); - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); if (slot.getConfig() == null) { if (mouseOverConfig(mouseX, mouseY)) { List hoverStringList = new ArrayList<>(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEFluidConfigSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEFluidConfigSlotWidget.java similarity index 92% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEFluidConfigSlotWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEFluidConfigSlotWidget.java index bbf171ffbe9..c5b3f16ebe4 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEFluidConfigSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEFluidConfigSlotWidget.java @@ -1,12 +1,10 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.slot; +package com.gregtechceu.gtceu.integration.ae2.gui.slot; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.misc.IGhostFluidTarget; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.ConfigWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; -import com.gregtechceu.gtceu.integration.ae2.utils.AEUtil; +import com.gregtechceu.gtceu.integration.ae2.gui.ConfigWidget; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableFluidSlot; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; import com.gregtechceu.gtceu.utils.FormattingUtil; import com.gregtechceu.gtceu.utils.GTMath; @@ -49,7 +47,7 @@ public AEFluidConfigSlotWidget(int x, int y, ConfigWidget widget, int index) { public void drawInBackground(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.drawInBackground(graphics, mouseX, mouseY, partialTicks); Position position = getPosition(); - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); GenericStack config = slot.getConfig(); GenericStack stock = slot.getStock(); drawSlots(graphics, mouseX, mouseY, position.x, position.y, parentWidget.isAutoPull()); @@ -148,7 +146,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { @Override public void handleClientAction(int id, FriendlyByteBuf buffer) { super.handleClientAction(id, buffer); - IConfigurableSlot slot = this.parentWidget.getConfig(this.index); + ConfigurableSlot slot = this.parentWidget.getConfig(this.index); if (id == REMOVE_ID) { slot.setConfig(null); this.parentWidget.disableAmount(); @@ -167,7 +165,7 @@ public void handleClientAction(int id, FriendlyByteBuf buffer) { if (id == AMOUNT_CHANGE_ID) { if (slot.getConfig() != null) { int amt = buffer.readInt(); - slot.setConfig(ExportOnlyAESlot.copy(slot.getConfig(), amt)); + slot.setConfig(ConfigurableSlot.copy(slot.getConfig(), amt)); writeUpdateInfo(AMOUNT_CHANGE_ID, buf -> buf.writeInt(amt)); } } @@ -186,7 +184,7 @@ public void handleClientAction(int id, FriendlyByteBuf buffer) { @Override public void readUpdateInfo(int id, FriendlyByteBuf buffer) { super.readUpdateInfo(id, buffer); - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); if (id == REMOVE_ID) { slot.setConfig(null); } @@ -198,7 +196,7 @@ public void readUpdateInfo(int id, FriendlyByteBuf buffer) { if (id == AMOUNT_CHANGE_ID) { if (slot.getConfig() != null) { int amt = buffer.readInt(); - slot.setConfig(ExportOnlyAESlot.copy(slot.getConfig(), amt)); + slot.setConfig(ConfigurableSlot.copy(slot.getConfig(), amt)); } } if (id == PICK_UP_ID) { @@ -212,7 +210,7 @@ public void readUpdateInfo(int id, FriendlyByteBuf buffer) { if (key.hasTag()) { stack.setTag(key.getTag().copy()); } - GenericStack stack1 = ExportOnlyAESlot.copy(slot.getStock(), + GenericStack stack1 = ConfigurableSlot.copy(slot.getStock(), Math.max(0, (slot.getStock().amount() - stack.getAmount()))); slot.setStock(stack1.amount() == 0 ? null : stack1); } @@ -244,7 +242,7 @@ public void acceptFluid(FluidStack fluidStack) { public boolean mouseWheelMove(double mouseX, double mouseY, double wheelDelta) { // Only allow the amount scrolling if not stocking, as amount is useless for stocking if (parentWidget.isStocking()) return false; - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); Rect2i rectangle = toRectangleBox(); rectangle.setHeight(rectangle.getHeight() / 2); if (slot.getConfig() == null || wheelDelta == 0 || !rectangle.contains((int) mouseX, (int) mouseY)) { @@ -270,8 +268,8 @@ public boolean mouseWheelMove(double mouseX, double mouseY, double wheelDelta) { } private int tryClickContainer(boolean isShiftKeyDown) { - ExportOnlyAEFluidSlot fluidTank = this.parentWidget - .getConfig(this.index) instanceof ExportOnlyAEFluidSlot fluid ? fluid : null; + ConfigurableFluidSlot fluidTank = this.parentWidget + .getConfig(this.index) instanceof ConfigurableFluidSlot fluid ? fluid : null; if (fluidTank == null) return -1; Player player = gui.entityPlayer; ItemStack currentStack = gui.getModularUIContainer().getCarried(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEItemConfigSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEItemConfigSlotWidget.java similarity index 93% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEItemConfigSlotWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEItemConfigSlotWidget.java index 574b4fdf276..8f34487a238 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEItemConfigSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEItemConfigSlotWidget.java @@ -1,10 +1,9 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.slot; +package com.gregtechceu.gtceu.integration.ae2.gui.slot; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.misc.IGhostItemTarget; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.ConfigWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAESlot; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; +import com.gregtechceu.gtceu.integration.ae2.gui.ConfigWidget; +import com.gregtechceu.gtceu.integration.ae2.slot.ConfigurableSlot; import com.lowdragmc.lowdraglib.gui.util.TextFormattingUtil; import com.lowdragmc.lowdraglib.utils.Position; @@ -35,7 +34,7 @@ public AEItemConfigSlotWidget(int x, int y, ConfigWidget widget, int index) { public void drawInBackground(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.drawInBackground(graphics, mouseX, mouseY, partialTicks); Position position = getPosition(); - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); GenericStack config = slot.getConfig(); GenericStack stock = slot.getStock(); drawSlots(graphics, mouseX, mouseY, position.x, position.y, parentWidget.isAutoPull()); @@ -127,7 +126,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { @Override public void handleClientAction(int id, FriendlyByteBuf buffer) { super.handleClientAction(id, buffer); - IConfigurableSlot slot = this.parentWidget.getConfig(this.index); + ConfigurableSlot slot = this.parentWidget.getConfig(this.index); if (id == REMOVE_ID) { slot.setConfig(null); this.parentWidget.disableAmount(); @@ -159,7 +158,7 @@ public void handleClientAction(int id, FriendlyByteBuf buffer) { stack.setTag(key.getTag().copy()); } this.gui.getModularUIContainer().setCarried(stack); - GenericStack stack1 = ExportOnlyAESlot.copy(slot.getStock(), + GenericStack stack1 = ConfigurableSlot.copy(slot.getStock(), Math.max(0, (slot.getStock().amount() - stack.getCount()))); slot.setStock(stack1.amount() == 0 ? null : stack1); writeUpdateInfo(PICK_UP_ID, buf -> {}); @@ -171,7 +170,7 @@ public void handleClientAction(int id, FriendlyByteBuf buffer) { @Override public void readUpdateInfo(int id, FriendlyByteBuf buffer) { super.readUpdateInfo(id, buffer); - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); if (id == REMOVE_ID) { slot.setConfig(null); } @@ -193,7 +192,7 @@ public void readUpdateInfo(int id, FriendlyByteBuf buffer) { stack.setTag(key.getTag().copy()); } this.gui.getModularUIContainer().setCarried(stack); - GenericStack stack1 = ExportOnlyAESlot.copy(slot.getStock(), + GenericStack stack1 = ConfigurableSlot.copy(slot.getStock(), Math.max(0, (slot.getStock().amount() - stack.getCount()))); slot.setStock(stack1.amount() == 0 ? null : stack1); } @@ -219,7 +218,7 @@ public void acceptItem(ItemStack itemStack) { public boolean mouseWheelMove(double mouseX, double mouseY, double wheelDelta) { // Only allow the amount scrolling if not stocking, as amount is useless for stocking if (parentWidget.isStocking()) return false; - IConfigurableSlot slot = this.parentWidget.getDisplay(this.index); + ConfigurableSlot slot = this.parentWidget.getDisplay(this.index); Rect2i rectangle = toRectangleBox(); rectangle.setHeight(rectangle.getHeight() / 2); if (slot.getConfig() == null || wheelDelta == 0 || !rectangle.contains((int) mouseX, (int) mouseY)) { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEPatternViewSlotWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEPatternViewSlotWidget.java similarity index 98% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEPatternViewSlotWidget.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEPatternViewSlotWidget.java index a3d0f477b93..465e9271be6 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/slot/AEPatternViewSlotWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/slot/AEPatternViewSlotWidget.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget.slot; +package com.gregtechceu.gtceu.integration.ae2.gui.slot; import com.gregtechceu.gtceu.api.gui.widget.SlotWidget; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEFluidConfigWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEFluidConfigWidget.java deleted file mode 100644 index 2ebe1b3164e..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEFluidConfigWidget.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget; - -import com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEFluidConfigSlotWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEFluidSlot; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; - -import appeng.api.stacks.GenericStack; - -public class AEFluidConfigWidget extends ConfigWidget { - - private final ExportOnlyAEFluidList fluidList; - - public AEFluidConfigWidget(int x, int y, ExportOnlyAEFluidList list) { - super(x, y, list.getInventory(), list.isStocking()); - this.fluidList = list; - } - - @Override - void init() { - int line; - this.displayList = new IConfigurableSlot[this.config.length]; - this.cached = new IConfigurableSlot[this.config.length]; - for (int index = 0; index < this.config.length; index++) { - this.displayList[index] = new ExportOnlyAEFluidSlot(); - this.cached[index] = new ExportOnlyAEFluidSlot(); - line = index / 8; - this.addWidget(new AEFluidConfigSlotWidget((index - line * 8) * 18, line * (18 * 2 + 2), this, index)); - } - } - - public boolean hasStackInConfig(GenericStack stack) { - return fluidList.hasStackInConfig(stack, true); - } - - public boolean isAutoPull() { - return fluidList.isAutoPull(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEItemConfigWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEItemConfigWidget.java deleted file mode 100644 index 167afbb34a5..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/gui/widget/AEItemConfigWidget.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.gui.widget; - -import com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEItemConfigSlotWidget; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemList; -import com.gregtechceu.gtceu.integration.ae2.slot.ExportOnlyAEItemSlot; -import com.gregtechceu.gtceu.integration.ae2.slot.IConfigurableSlot; - -import appeng.api.stacks.GenericStack; - -public class AEItemConfigWidget extends ConfigWidget { - - private final ExportOnlyAEItemList itemList; - - public AEItemConfigWidget(int x, int y, ExportOnlyAEItemList list) { - super(x, y, list.getInventory(), list.isStocking()); - this.itemList = list; - } - - @Override - void init() { - int line; - this.displayList = new IConfigurableSlot[this.config.length]; - this.cached = new IConfigurableSlot[this.config.length]; - for (int index = 0; index < this.config.length; index++) { - this.displayList[index] = new ExportOnlyAEItemSlot(); - this.cached[index] = new ExportOnlyAEItemSlot(); - line = index / 8; - this.addWidget(new AEItemConfigSlotWidget((index - line * 8) * 18, line * (18 * 2 + 2), this, index)); - } - } - - public boolean hasStackInConfig(GenericStack stack) { - return itemList.hasStackInConfig(stack, true); - } - - public boolean isAutoPull() { - return itemList.isAutoPull(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java index 6914f7782a3..0270e3f5fb7 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java @@ -1,187 +1,261 @@ package com.gregtechceu.gtceu.integration.ae2.machine; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.IManagedGridNode; +import appeng.api.stacks.AEItemKey; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; -import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.list.AEListGridWidget; -import com.gregtechceu.gtceu.integration.ae2.utils.KeyStorage; - +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; +import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; +import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; +import lombok.Setter; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; - -import appeng.api.config.Actionable; -import appeng.api.stacks.AEItemKey; -import lombok.NoArgsConstructor; +import net.minecraft.world.item.crafting.Ingredient; import org.jetbrains.annotations.NotNull; -import java.util.Collections; import java.util.List; +import java.util.function.IntFunction; +import java.util.function.Predicate; -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * The Output Bus that can directly send its contents to ME storage network. - */ -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class MEOutputBusPartMachine extends MEBusPartMachine implements IMachineLife, IInteractedMachine { +public class MEOutputBusPartMachine extends MEBusPartMachine { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - MEOutputBusPartMachine.class, MEBusPartMachine.MANAGED_FIELD_HOLDER); + MEOutputBusPartMachine.class, + MEBusPartMachine.MANAGED_FIELD_HOLDER + ); @Persisted - private KeyStorage internalBuffer; // Do not use KeyCounter, use our simple implementation + private final AEKeyStorage storage = new AEKeyStorage(); - public MEOutputBusPartMachine(IMachineBlockEntity holder, Object... args) { - super(holder, IO.OUT, args); + public MEOutputBusPartMachine(IMachineBlockEntity holder) { + this(holder, holder.getDefinition().getTier()); } - ///////////////////////////////// - // ***** Machine LifeCycle ****// - ///////////////////////////////// + public MEOutputBusPartMachine(IMachineBlockEntity holder, int tier, Object... args) { + super(holder, tier, IO.OUT); + this.inventory = new InaccessibleInfiniteHandler(this); + } @Override - protected NotifiableItemStackHandler createInventory(Object... args) { - this.internalBuffer = new KeyStorage(); - return new InaccessibleInfiniteHandler(this); + public void onLoad() { + super.onLoad(); + hookInternalBufferListener(); + updateSubscription(); } @Override - public void onMachineRemoved() { - var grid = getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) { - for (var entry : internalBuffer) { - grid.getStorageService().getInventory().insert(entry.getKey(), entry.getLongValue(), - Actionable.MODULATE, actionSource); - } - } + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + IGridConnectedMachine.super.onMainNodeStateChanged(reason); + updateSubscription(); } @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; + public void setWorkingEnabled(boolean workingEnabled) { + super.setWorkingEnabled(workingEnabled); + updateSubscription(); } - ///////////////////////////////// - // ********** Sync ME *********// - ///////////////////////////////// - @Override - protected boolean shouldSubscribe() { - return super.shouldSubscribe() && !internalBuffer.storage.isEmpty(); + protected void updateSubscription() { + IManagedGridNode node = nodeHost.getMainNode(); + if (isWorkingEnabled() && node.isActive() && !internalBuffer.isEmpty()) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + } else if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; + } } - @Override - public void autoIO() { - if (!this.shouldSyncME()) return; - if (this.updateMEStatus()) { - var grid = getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) { - internalBuffer.insertInventory(grid.getStorageService().getInventory(), actionSource); - } - this.updateInventorySubscription(); + protected void autoIO() { + var grid = nodeHost.getMainNode().getGrid(); + if (grid != null && !internalBuffer.isEmpty()) { + internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); } } - /////////////////////////////// - // ********** GUI ***********// - /////////////////////////////// + @Override + public void onMachineRemoved() { + var grid = nodeHost.getMainNode().getGrid(); + if (grid != null && !internalBuffer.isEmpty()) + internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); + } @Override public Widget createUIWidget() { WidgetGroup group = new WidgetGroup(0, 0, 170, 65); - // ME Network status - group.addWidget(new LabelWidget(5, 0, () -> this.isOnline ? - "gtceu.gui.me_network.online" : - "gtceu.gui.me_network.offline")); + group.addWidget(new LabelWidget(5, 0, () -> nodeHost.getMainNode().isActive() ? + "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); group.addWidget(new LabelWidget(5, 10, "gtceu.gui.waiting_list")); - // display list group.addWidget(new AEListGridWidget.Item(5, 20, 3, this.internalBuffer)); - return group; } - private class InaccessibleInfiniteHandler extends NotifiableItemStackHandler { + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + private void hookInternalBufferListener() { + if (internalBufferListenerHooked) return; + internalBufferListenerHooked = true; - public InaccessibleInfiniteHandler(MetaMachine holder) { - super(holder, 1, IO.OUT, IO.NONE, ItemStackHandlerDelegate::new); - internalBuffer.setOnContentsChanged(this::onContentsChanged); - } + Runnable previous = internalBuffer.getOnContentsChanged(); + internalBuffer.setOnContentsChanged(() -> { + if (previous != null) previous.run(); + inventory.onContentsChanged(); + updateSubscription(); + }); + } - @Override - public @NotNull List getContents() { - return Collections.emptyList(); - } + static class Adapter extends NotifiableItemStackHandler { - @Override - public double getTotalContentAmount() { - return 0; + private static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + Adapter.class, + NotifiableItemStackHandler.MANAGED_FIELD_HOLDER + ); + + private final AEKeyStorage storage; + @Setter + private Predicate filter = stack -> true; + + public Adapter(MetaMachine machine, AEKeyStorage storage) { + super(machine, 0, IO.OUT, IO.NONE); + this.storage = storage; + this.storage.setOnContentsChanged(this::onContentsChanged); } @Override - public boolean isEmpty() { - return true; - } - } + public List handleRecipeInner(IO io, GTRecipe recipe, List left, boolean simulate) { + if (io != handlerIO) return left; + if (io != IO.IN && io != IO.OUT) return left.isEmpty() ? null : left; + + // Temporarily remove listener so that we can broadcast the entire set of transactions once + Runnable listener = storage.getOnContentsChanged(); + storage.setOnContentsChanged(() -> {}); + boolean changed = false; + + // Store the ItemStack in each slot after an operation + // Necessary for simulation since we don't actually modify the slot's contents + // Doesn't hurt for execution, and definitely cheaper than copying the entire storage + ItemStack[] visited = new ItemStack[storage.getSlots()]; + for (var it = left.listIterator(); it.hasNext();) { + var ingredient = it.next(); + if (ingredient.isEmpty()) { + it.remove(); + continue; + } - @NoArgsConstructor - private class ItemStackHandlerDelegate extends CustomItemStackHandler { + ItemStack[] items; + int amount; + if (ingredient instanceof IntProviderIngredient provider) { + provider.setItemStacks(null); + provider.setSampledCount(-1); + + ItemStack output; + if (simulate) { + output = provider.getMaxSizeStack(); + items = new ItemStack[] { output }; + } else { + items = provider.getItems(); + if (items.length == 0 || items[0].isEmpty()) { + it.remove(); + continue; + } + output = items[0]; + } + amount = output.getCount(); + } else { + items = ingredient.getItems(); + if (items.length == 0 || items[0].isEmpty()) { + it.remove(); + continue; + } + if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); + else amount = items[0].getCount(); + } - // Necessary for InaccessibleInfiniteHandler - public ItemStackHandlerDelegate(Integer integer) { - super(); - } + for (int slot = 0; slot < storage.getSlots(); ++slot) { + ItemStack current = visited[slot] == null ? storage.getStackInSlot(slot) : visited[slot]; + int count = current.getCount(); + + ItemStack output = items[0].copyWithCount(amount); + // Only try this slot if not visited or if visited with the same type of item + if (visited[slot] == null || GTUtil.isSameItemSameTags(visited[slot], output)) { + if (count < output.getMaxStackSize() && count < storage.getSlotLimit(slot)) { + var remainder = getActioned(storage, slot, recipe.ingredientActions); + if (remainder == null) remainder = storage.insertItem(slot, output, simulate); + if (remainder.getCount() < amount) { + changed = true; + visited[slot] = output.copyWithCount(count + amount - remainder.getCount()); + } + amount = remainder.getCount(); + } + } + + if (amount <= 0) { + it.remove(); + break; + } + } + // Modify ingredient if we didn't finish it off + if (amount > 0) { + if (ingredient instanceof SizedIngredient si) { + si.setAmount(amount); + } else { + items[0].setCount(amount); + } + } + } - @Override - public int getSlots() { - return Short.MAX_VALUE; + storage.setOnContentsChanged(listener); + if (changed && !simulate) listener.run(); + + return left.isEmpty() ? null : left; } @Override - public int getSlotLimit(int slot) { - return Integer.MAX_VALUE; + public List getContents() { + return storage.stream() + .map(entry -> { + AEItemKey key = (AEItemKey) entry.getKey(); + int amount = entry.getIntValue(); + return key.toStack(amount); + }) + .toList(); } @Override - public ItemStack getStackInSlot(int slot) { - return ItemStack.EMPTY; + public double getTotalContentAmount() { + return storage.values().intStream().sum(); } @Override - public void setStackInSlot(int slot, ItemStack stack) { - // NO-OP + public int getSize() { + return storage.size(); } @Override - public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { - var key = AEItemKey.of(stack); - int count = stack.getCount(); - long oldValue = internalBuffer.storage.getOrDefault(key, 0); - long changeValue = Math.min(Long.MAX_VALUE - oldValue, count); - if (changeValue > 0) { - if (!simulate) { - internalBuffer.storage.put(key, oldValue + changeValue); - internalBuffer.onChanged(); - } - return stack.copyWithCount((int) (count - changeValue)); - } else { - return ItemStack.EMPTY; - } + public boolean isEmpty() { + return storage.isEmpty(); } @Override - public ItemStack extractItem(int slot, int amount, boolean simulate) { - return ItemStack.EMPTY; + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; } } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java index 560cf98d83a..5680fd82d00 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java @@ -1,135 +1,143 @@ package com.gregtechceu.gtceu.integration.ae2.machine; +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.IManagedGridNode; +import appeng.api.stacks.AEFluidKey; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.TickableSubscription; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; -import com.gregtechceu.gtceu.integration.ae2.gui.widget.list.AEListGridWidget; -import com.gregtechceu.gtceu.integration.ae2.utils.KeyStorage; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; +import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; +import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; import com.gregtechceu.gtceu.utils.GTMath; - import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; +import lombok.Getter; import net.minecraftforge.fluids.FluidStack; - -import appeng.api.config.Actionable; -import appeng.api.stacks.AEFluidKey; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; -import javax.annotation.ParametersAreNonnullByDefault; - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class MEOutputHatchPartMachine extends MEHatchPartMachine implements IMachineLife { +public class MEOutputHatchPartMachine extends MEHatchPartMachine { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - MEOutputHatchPartMachine.class, MEHatchPartMachine.MANAGED_FIELD_HOLDER); + MEOutputHatchPartMachine.class, + MEHatchPartMachine.MANAGED_FIELD_HOLDER + ); @Persisted - private KeyStorage internalBuffer; // Do not use KeyCounter, use our simple implementation + private AEKeyStorage internalBuffer; + @Getter + @Persisted + private final NotifiableFluidTank tank; + @Nullable + protected TickableSubscription autoIOSubs; + private boolean internalBufferListenerHooked; - public MEOutputHatchPartMachine(IMachineBlockEntity holder, Object... args) { - super(holder, IO.OUT, args); + public MEOutputHatchPartMachine(IMachineBlockEntity holder) { + this(holder, holder.getDefinition().getTier(), 0, 0); } - ///////////////////////////////// - // ***** Machine LifeCycle ****// - ///////////////////////////////// - - @Override - protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object... args) { - this.internalBuffer = new KeyStorage(); - return new InaccessibleInfiniteTank(this); + public MEOutputHatchPartMachine(IMachineBlockEntity holder, int tier, int initialTankCapacity, int slots, Object... args) { + super(holder, tier, IO.OUT); + this.internalBuffer = new AEKeyStorage(); + this.tank = new InaccessibleInfiniteTank(this); } @Override public void onLoad() { super.onLoad(); - if (isRemote()) return; + hookInternalBufferListener(); + updateSubscription(); } @Override - public void onMachineRemoved() { - var grid = getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) { - for (var entry : internalBuffer) { - grid.getStorageService().getInventory().insert(entry.getKey(), entry.getLongValue(), - Actionable.MODULATE, actionSource); - } - } + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + IGridConnectedMachine.super.onMainNodeStateChanged(reason); + updateSubscription(); } @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; + public void setWorkingEnabled(boolean workingEnabled) { + super.setWorkingEnabled(workingEnabled); + updateSubscription(); } - ///////////////////////////////// - // ********** Sync ME *********// - ///////////////////////////////// - @Override - protected boolean shouldSubscribe() { - return super.shouldSubscribe() && !internalBuffer.storage.isEmpty(); + protected void updateSubscription() { + IManagedGridNode node = nodeHost.getMainNode(); + if (isWorkingEnabled() && node.isActive() && !internalBuffer.isEmpty()) { + autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); + } else if (autoIOSubs != null) { + autoIOSubs.unsubscribe(); + autoIOSubs = null; + } } - @Override protected void autoIO() { - if (!this.shouldSyncME()) return; - if (this.updateMEStatus()) { - var grid = getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) { - internalBuffer.insertInventory(grid.getStorageService().getInventory(), actionSource); - } - this.updateTankSubscription(); + var grid = nodeHost.getMainNode().getGrid(); + if (grid != null && !internalBuffer.isEmpty()) { + internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); } } - /////////////////////////////// - // ********** GUI ***********// - /////////////////////////////// + @Override + public void onMachineRemoved() { + var grid = nodeHost.getMainNode().getGrid(); + if (grid != null && !internalBuffer.isEmpty()) + internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); + } @Override public Widget createUIWidget() { WidgetGroup group = new WidgetGroup(0, 0, 170, 65); - // ME Network status - group.addWidget(new LabelWidget(5, 0, () -> this.isOnline ? - "gtceu.gui.me_network.online" : - "gtceu.gui.me_network.offline")); + group.addWidget(new LabelWidget(5, 0, () -> nodeHost.getMainNode().isActive() ? + "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); group.addWidget(new LabelWidget(5, 10, "gtceu.gui.waiting_list")); - // display list group.addWidget(new AEListGridWidget.Fluid(5, 20, 3, this.internalBuffer)); - return group; } + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + private void hookInternalBufferListener() { + if (internalBufferListenerHooked) return; + internalBufferListenerHooked = true; + + Runnable previous = internalBuffer.getOnContentsChanged(); + internalBuffer.setOnContentsChanged(() -> { + if (previous != null) previous.run(); + tank.onContentsChanged(); + updateSubscription(); + }); + } + private class InaccessibleInfiniteTank extends NotifiableFluidTank { - FluidStorageDelegate storage; + private final FluidStorageDelegate storage; public InaccessibleInfiniteTank(MetaMachine holder) { super(holder, List.of(new FluidStorageDelegate()), IO.OUT, IO.NONE); - internalBuffer.setOnContentsChanged(this::onContentsChanged); storage = (FluidStorageDelegate) getStorages()[0]; allowSameFluids = true; } @Override public int getTanks() { - return 128; + return 1; } @Override @@ -177,13 +185,11 @@ public List handleRecipeInner(IO io, GTRecipe recipe, List proxies = new ObjectOpenHashSet<>(); - private final Set proxyMachines = new ReferenceOpenHashSet<>(); + private final Set proxyMachines = new ReferenceOpenHashSet<>(); @Getter protected final InternalSlotRecipeHandler internalRecipeHandler; @@ -219,22 +217,22 @@ protected void update() { } } - public void addProxy(MEPatternBufferProxyPartMachine proxy) { + public void addProxy(MEPatternProxyPartMachine proxy) { proxies.add(proxy.getPos()); proxyMachines.add(proxy); } - public void removeProxy(MEPatternBufferProxyPartMachine proxy) { + public void removeProxy(MEPatternProxyPartMachine proxy) { proxies.remove(proxy.getPos()); proxyMachines.remove(proxy); } @UnmodifiableView - public Set getProxies() { + public Set getProxies() { if (proxyMachines.size() != proxies.size()) { proxyMachines.clear(); for (var pos : proxies) { - if (MetaMachine.getMachine(getLevel(), pos) instanceof MEPatternBufferProxyPartMachine proxy) { + if (MetaMachine.getMachine(getLevel(), pos) instanceof MEPatternProxyPartMachine proxy) { proxyMachines.add(proxy); } } @@ -318,7 +316,7 @@ public Widget createUIWidget() { group.addWidget(new LabelWidget( 8, 2, - () -> this.isOnline ? "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); + () -> this.online ? "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); group.addWidget(new AETextInputButtonWidget(18 * rowSize + 8 - 70, 2, 70, 10) .setText(customName) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEPatternBufferProxyPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEPatternProxyPartMachine.java similarity index 90% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEPatternBufferProxyPartMachine.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEPatternProxyPartMachine.java index 1e8901b2b8b..5c36d6c5d10 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEPatternBufferProxyPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEPatternProxyPartMachine.java @@ -8,14 +8,13 @@ import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredIOPartMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeHandlerList; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.ProxySlotRecipeHandler; +import com.gregtechceu.gtceu.integration.ae2.utils.ProxySlotRecipeHandler; import com.lowdragmc.lowdraglib.gui.modular.ModularUI; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.nbt.Tag; import net.minecraft.server.TickTask; @@ -33,13 +32,11 @@ import javax.annotation.ParametersAreNonnullByDefault; -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class MEPatternBufferProxyPartMachine extends TieredIOPartMachine +public class MEPatternProxyPartMachine extends TieredIOPartMachine implements IMachineLife, IDataStickInteractable { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - MEPatternBufferProxyPartMachine.class, TieredIOPartMachine.MANAGED_FIELD_HOLDER); + MEPatternProxyPartMachine.class, TieredIOPartMachine.MANAGED_FIELD_HOLDER); @Getter private final ProxySlotRecipeHandler proxySlotRecipeHandler; @@ -52,7 +49,7 @@ public class MEPatternBufferProxyPartMachine extends TieredIOPartMachine private @Nullable MEPatternBufferPartMachine buffer = null; private boolean bufferResolved = false; - public MEPatternBufferProxyPartMachine(IMachineBlockEntity holder) { + public MEPatternProxyPartMachine(IMachineBlockEntity holder) { super(holder, GTValues.LuV, IO.IN); proxySlotRecipeHandler = new ProxySlotRecipeHandler(this, MEPatternBufferPartMachine.MAX_PATTERN_COUNT); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index 54b5f307991..c8e4bd63d4c 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -56,7 +56,7 @@ public class MEStockingBusPartMachine extends MEBusPartMachine implements IDataS @Getter @Setter @Persisted - private int minStackSize = 1; + private int minStackSize = 1; //TODO make configurable @Persisted protected final StockingConfigHandler configHandler; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java new file mode 100644 index 00000000000..7c9f6a59848 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java @@ -0,0 +1,136 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.AEKey; +import appeng.api.storage.MEStorage; +import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import java.util.Iterator; +import java.util.stream.Stream; + +/** + * Storage for AE2 keys with associated amounts. + * Supports serialization, change tracking, and inventory operations. + */ +public class AEKeyStorage implements Iterable>, ITagSerializable, IContentChangeAware { + + private final Object2IntMap storage = new Object2IntLinkedOpenHashMap<>(); + + @Getter + @Setter + private Runnable onContentsChanged = () -> { + }; + + public void add(AEKey key, int amount) { + if (amount <= 0) return; + storage.mergeInt(key, amount, Integer::sum); + onContentsChanged.run(); + } + + public int get(AEKey key) { + return storage.getInt(key); + } + + public void remove(AEKey key) { + if (storage.removeInt(key) != 0) { + onContentsChanged.run(); + } + } + + public void clear() { + if (!storage.isEmpty()) { + storage.clear(); + onContentsChanged.run(); + } + } + + public boolean isEmpty() { + return storage.isEmpty(); + } + + public int size() { + return storage.size(); + } + + public ObjectSet keySet() { + return storage.keySet(); + } + + public IntCollection values() { + return storage.values(); + } + + public Stream> stream() { + return storage.object2IntEntrySet().stream(); + } + + /** + * Transfers stored stacks into the given inventory. + * Removes fully transferred entries and updates partial transfers. + */ + public void transferTo(MEStorage inventory, IActionSource source) { + if (storage.isEmpty()) return; + + var it = storage.object2IntEntrySet().iterator(); + boolean changed = false; + + while (it.hasNext()) { + var entry = it.next(); + int transferred = Math.toIntExact( + inventory.insert(entry.getKey(), entry.getIntValue(), Actionable.MODULATE, source) + ); + if (transferred > 0) { + changed = true; + int remaining = entry.getIntValue() - transferred; + if (remaining <= 0) { + it.remove(); + } else { + entry.setValue(remaining); + } + } + } + + if (changed) { + onContentsChanged.run(); + } + } + + @Override + public Iterator> iterator() { + return storage.object2IntEntrySet().iterator(); + } + + @Override + public ListTag serializeNBT() { + var list = new ListTag(); + for (var entry : storage.object2IntEntrySet()) { + var tag = new CompoundTag(); + tag.put("key", entry.getKey().toTagGeneric()); + tag.putInt("amount", entry.getIntValue()); + list.add(tag); + } + return list; + } + + @Override + public void deserializeNBT(ListTag tags) { + storage.clear(); + for (int i = 0; i < tags.size(); i++) { + var tag = tags.getCompound(i); + var key = AEKey.fromTagGeneric(tag.getCompound("key")); + if (key != null) { + storage.put(key, tag.getInt("amount")); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/DynamicItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/DynamicItemStackHandler.java new file mode 100644 index 00000000000..6c2fb40daf6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/DynamicItemStackHandler.java @@ -0,0 +1,173 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.ItemHandlerHelper; + +import java.util.ArrayList; +import java.util.Objects; + +public class DynamicItemStackHandler extends CustomItemStackHandler { + + private static final NonNullList EMPTY_LIST = NonNullList.withSize(0, ItemStack.EMPTY); + + private final ArrayList stacks; + + public DynamicItemStackHandler() { + super(EMPTY_LIST); + this.stacks = new ArrayList<>(); + } + + @Override + public void setSize(int size) { + // no op + } + + @Override + public int getSlots() { + return stacks.size(); + } + + @Override + public ItemStack getStackInSlot(int slot) { + validateSlotIndex(slot); + return Objects.requireNonNullElse(stacks.get(slot), ItemStack.EMPTY); + } + + @Override + public void setStackInSlot(int slot, ItemStack stack) { + ensureCapacity(slot); + validateSlotIndex(slot); + stacks.set(slot, stack); + onContentsChanged(slot); + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + if (stack.isEmpty()) return ItemStack.EMPTY; + if (!isItemValid(slot, stack)) return stack; + + ensureCapacity(slot); + validateSlotIndex(slot); + + ItemStack existing = stacks.get(slot); + int limit = getStackLimit(slot, stack); + + if (!existing.isEmpty()) { + if (!ItemHandlerHelper.canItemStacksStack(stack, existing)) return stack; + limit -= existing.getCount(); + } + + if (limit <= 0) return stack; + + boolean reachedLimit = stack.getCount() > limit; + + if (!simulate) { + if (existing.isEmpty()) { + stacks.set(slot, reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack); + } else { + existing.grow(reachedLimit ? limit : stack.getCount()); + } + onContentsChanged(slot); + } + + return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.getCount() - limit) : ItemStack.EMPTY; + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + if (amount == 0) return ItemStack.EMPTY; + + validateSlotIndex(slot); + + ItemStack existing = stacks.get(slot); + if (existing.isEmpty()) return ItemStack.EMPTY; + + int toExtract = Math.min(amount, existing.getMaxStackSize()); + + if (existing.getCount() <= toExtract) { + if (!simulate) { + stacks.set(slot, ItemStack.EMPTY); + onContentsChanged(slot); + return existing; + } + return existing.copy(); + } + + if (!simulate) { + stacks.set(slot, ItemHandlerHelper.copyStackWithSize(existing, existing.getCount() - toExtract)); + onContentsChanged(slot); + } + + return ItemHandlerHelper.copyStackWithSize(existing, toExtract); + } + + @Override + public void clear() { + stacks.clear(); + onContentsChanged.run(); + } + + @Override + public int getSlotLimit(int slot) { + return Integer.MAX_VALUE; + } + + @Override + protected int getStackLimit(int slot, ItemStack stack) { + return Integer.MAX_VALUE; + } + + @Override + protected void validateSlotIndex(int slot) { + if (slot < 0 || slot >= stacks.size()) { + throw new RuntimeException("Slot " + slot + " not in valid range - [0," + stacks.size() + ")"); + } + } + + @Override + public CompoundTag serializeNBT() { + var nbt = new CompoundTag(); + var items = new ListTag(); + for (int i = 0; i < stacks.size(); i++) { + var stack = stacks.get(i); + if (!stack.isEmpty()) { + var itemTag = new CompoundTag(); + itemTag.putInt("Slot", i); + stack.save(itemTag); + items.add(itemTag); + } + } + nbt.put("Items", items); + nbt.putInt("Size", stacks.size()); + return nbt; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + int size = nbt.getInt("Size"); + stacks.clear(); + ensureCapacity(size); + + var items = nbt.getList("Items", Tag.TAG_COMPOUND); + for (int i = 0; i < items.size(); i++) { + var itemTag = items.getCompound(i); + int slot = itemTag.getInt("Slot"); + if (slot >= 0 && slot < size) { + stacks.set(slot, ItemStack.of(itemTag)); + } + } + onLoad(); + } + + protected void ensureCapacity(int slot) { + while (stacks.size() <= slot) { + stacks.add(ItemStack.EMPTY); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/InternalSlotRecipeHandler.java similarity index 98% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/InternalSlotRecipeHandler.java index 4a2e4e49431..42bdb3ee1b4 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/InternalSlotRecipeHandler.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.integration.ae2.machine.trait; +package com.gregtechceu.gtceu.integration.ae2.utils; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.trait.NotifiableRecipeHandlerTrait; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/KeyStorage.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/KeyStorage.java deleted file mode 100644 index 81bb00f1216..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/KeyStorage.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.utils; - -import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; -import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; - -import appeng.api.config.Actionable; -import appeng.api.networking.security.IActionSource; -import appeng.api.stacks.AEKey; -import appeng.api.storage.MEStorage; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; -import lombok.Getter; -import lombok.Setter; -import org.jetbrains.annotations.Nullable; - -import java.util.Iterator; - -/** - * Used to store {@link appeng.api.stacks.GenericStack } in a way that associates key and amount. - * Provides methods for serialization and deserialization. - */ -@MethodsReturnNonnullByDefault -public class KeyStorage implements ITagSerializable, IContentChangeAware, - Iterable> { - - public final Object2LongMap storage = new Object2LongOpenHashMap<>(); // TODO trim periodically or not - - @Nullable - @Getter - @Setter - private Runnable onContentsChanged; - - /** - * Insert the stacks into the inventory as much as possible - * - * @param inventory the inventory into which stacks will be inserted - * @param source the source of the action - */ - public void insertInventory(MEStorage inventory, IActionSource source) { - var it = iterator(); - boolean changed = false; - while (it.hasNext()) { - var entry = it.next(); - var key = entry.getKey(); - var amount = entry.getLongValue(); - long inserted = inventory.insert(key, amount, Actionable.MODULATE, - source); - if (inserted > 0) { - changed = true; - if (inserted >= amount) { - it.remove(); - } else { - entry.setValue(amount - inserted); - } - } - } - if (changed) { - onChanged(); - } - } - - public void onChanged() { - if (onContentsChanged != null) { - onContentsChanged.run(); - } - } - - @Override - public ListTag serializeNBT() { - var list = new ListTag(); - for (var entry : storage.object2LongEntrySet()) { - var tag = new CompoundTag(); - tag.put("key", entry.getKey().toTagGeneric()); - tag.putLong("value", entry.getLongValue()); - list.add(tag); - } - return list; - } - - @Override - public void deserializeNBT(ListTag tags) { - for (int i = 0; i < tags.size(); i++) { - var tag = tags.getCompound(i); - var key = AEKey.fromTagGeneric(tag.getCompound("key")); - long value = tag.getLong("value"); - storage.put(key, value); - } - } - - @Override - public Iterator> iterator() { - return storage.object2LongEntrySet().iterator(); - } - - public boolean isEmpty() { - return storage.isEmpty(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/ProxySlotRecipeHandler.java similarity index 94% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/ProxySlotRecipeHandler.java index 4d40ca03ebe..59195dab82c 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/ProxySlotRecipeHandler.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.integration.ae2.machine.trait; +package com.gregtechceu.gtceu.integration.ae2.utils; import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.MetaMachine; @@ -9,8 +9,8 @@ import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferProxyPartMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.InternalSlotRecipeHandler.SlotRHL; +import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternProxyPartMachine; +import com.gregtechceu.gtceu.integration.ae2.utils.InternalSlotRecipeHandler.SlotRHL; import com.lowdragmc.lowdraglib.syncdata.ISubscription; @@ -28,7 +28,7 @@ public final class ProxySlotRecipeHandler { @Getter private final List proxySlotHandlers; - public ProxySlotRecipeHandler(MEPatternBufferProxyPartMachine machine, int slots) { + public ProxySlotRecipeHandler(MEPatternProxyPartMachine machine, int slots) { proxySlotHandlers = new ArrayList<>(slots); for (int i = 0; i < slots; ++i) { proxySlotHandlers.add(new ProxyRHL(machine)); @@ -58,7 +58,7 @@ private static class ProxyRHL extends RecipeHandlerList { private final ProxyFluidRecipeHandler sharedFluid; private final ProxyFluidRecipeHandler slotFluid; - public ProxyRHL(MEPatternBufferProxyPartMachine machine) { + public ProxyRHL(MEPatternProxyPartMachine machine) { super(IO.IN); circuit = new ProxyItemRecipeHandler(machine); sharedItem = new ProxyItemRecipeHandler(machine); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTFluidStorageProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTFluidStorageProvider.java index 69b085bb8a5..af25e4aa08d 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTFluidStorageProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTFluidStorageProvider.java @@ -6,7 +6,7 @@ import com.gregtechceu.gtceu.common.machine.storage.CreativeTankMachine; import com.gregtechceu.gtceu.common.machine.storage.QuantumTankMachine; import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferProxyPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternProxyPartMachine; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; @@ -74,7 +74,7 @@ public List> getClientGroups(Accessor accessor, Li list.add(JadeForgeUtils.fromFluidStack(stack, capacity)); } return list.isEmpty() ? List.of() : List.of(new ViewGroup<>(list)); - } else if (GTCEu.Mods.isAE2Loaded() && machine instanceof MEPatternBufferProxyPartMachine proxy) { + } else if (GTCEu.Mods.isAE2Loaded() && machine instanceof MEPatternProxyPartMachine proxy) { var buffer = proxy.getBuffer(); if (buffer == null) return Collections.emptyList(); return FluidStorageProvider.INSTANCE.getGroups(serverPlayer, serverLevel, buffer.holder, b); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTItemStorageProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTItemStorageProvider.java index 6fbb859903f..68d5071f015 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTItemStorageProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/GTItemStorageProvider.java @@ -5,7 +5,7 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.common.machine.storage.CreativeChestMachine; import com.gregtechceu.gtceu.common.machine.storage.QuantumChestMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferProxyPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternProxyPartMachine; import com.gregtechceu.gtceu.utils.GTMath; import net.minecraft.resources.ResourceLocation; @@ -61,7 +61,7 @@ public List> getClientGroups(Accessor accessor, Lis list.add(stored.copyWithCount(stack)); } return list.isEmpty() ? Collections.emptyList() : List.of(new ViewGroup<>(list)); - } else if (machine instanceof MEPatternBufferProxyPartMachine proxy) { + } else if (machine instanceof MEPatternProxyPartMachine proxy) { var buffer = proxy.getBuffer(); if (buffer == null) return Collections.emptyList(); return ItemStorageProvider.INSTANCE.getGroups(serverPlayer, serverLevel, buffer.holder, b); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MEPatternBufferProxyProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MEPatternBufferProxyProvider.java index 562b3af3d11..4d2f7e1e50b 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MEPatternBufferProxyProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MEPatternBufferProxyProvider.java @@ -3,7 +3,7 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.client.util.TooltipHelper; -import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferProxyPartMachine; +import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternProxyPartMachine; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; @@ -21,7 +21,7 @@ public class MEPatternBufferProxyProvider implements IBlockComponentProvider, IS @Override public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { - if (blockEntity.getMetaMachine() instanceof MEPatternBufferProxyPartMachine) { + if (blockEntity.getMetaMachine() instanceof MEPatternProxyPartMachine) { CompoundTag serverData = blockAccessor.getServerData(); if (!serverData.getBoolean("formed")) return; if (!serverData.getBoolean("bound")) { @@ -41,7 +41,7 @@ public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPlugi @Override public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { - if (blockEntity.getMetaMachine() instanceof MEPatternBufferProxyPartMachine proxy) { + if (blockEntity.getMetaMachine() instanceof MEPatternProxyPartMachine proxy) { if (!proxy.isFormed()) { compoundTag.putBoolean("formed", false); return; From ba1f5b676383eb197eda5ac7529be550cb3b78ab Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Thu, 26 Mar 2026 15:09:04 +0800 Subject: [PATCH 12/21] refactor ME stocking bus/hatch config handling --- .../ae2/machine/MEStockingBusPartMachine.java | 8 ++-- .../machine/MEStockingHatchPartMachine.java | 16 ++++++- .../integration/ae2/utils/MEConfigUtil.java | 45 ++++++++++++------- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index c8e4bd63d4c..7ea5f786db5 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -56,7 +56,7 @@ public class MEStockingBusPartMachine extends MEBusPartMachine implements IDataS @Getter @Setter @Persisted - private int minStackSize = 1; //TODO make configurable + private int minStackSize = 1; @Persisted protected final StockingConfigHandler configHandler; @@ -159,21 +159,23 @@ public Component getConfigName() { @Override public void writeConfig(CompoundTag tag) { + MEConfigUtil.writeGhostCircuit(tag, circuitInventory); MEConfigUtil.writeAutoPull(tag, autoPull); if (!autoPull) { MEConfigUtil.writeConfigHandler(tag, configHandler); } - MEConfigUtil.writeGhostCircuit(tag, circuitInventory); + MEConfigUtil.writeMinStackSize(tag, minStackSize); } @Override public void readConfig(CompoundTag tag) { + MEConfigUtil.readGhostCircuit(tag, circuitInventory); MEConfigUtil.readAutoPull(tag, this::setAutoPull); if (!autoPull) { MEConfigUtil.readConfigHandler(tag, configHandler); getInventory().onContentsChanged(); } - MEConfigUtil.readGhostCircuit(tag, circuitInventory); + MEConfigUtil.readMinStackSize(tag, this::setMinStackSize); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index 6b482bcca8f..7b0674d81ae 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -150,21 +150,23 @@ public Component getConfigName() { @Override public void writeConfig(CompoundTag tag) { + MEConfigUtil.writeGhostCircuit(tag, circuitInventory); MEConfigUtil.writeAutoPull(tag, autoPull); if (!autoPull) { MEConfigUtil.writeConfigHandler(tag, configHandler); } - MEConfigUtil.writeGhostCircuit(tag, circuitInventory); + MEConfigUtil.writeMinStackSize(tag, minStackSize); } @Override public void readConfig(CompoundTag tag) { + MEConfigUtil.readGhostCircuit(tag, circuitInventory); MEConfigUtil.readAutoPull(tag, this::setAutoPull); if (!autoPull) { MEConfigUtil.readConfigHandler(tag, configHandler); tank.onContentsChanged(); } - MEConfigUtil.readGhostCircuit(tag, circuitInventory); + MEConfigUtil.readMinStackSize(tag, this::setMinStackSize); } @Override @@ -237,6 +239,16 @@ public FluidStack drain(int maxDrain, FluidAction action) { return extract(key, maxDrain, action); } + @Override + public boolean isEmpty() { + return getFluid().isEmpty(); + } + + @Override + public int getSpace() { + return Math.max(0, capacity - getFluid().getAmount()); + } + private FluidStack extract(AEFluidKey key, int amount, FluidAction action) { IManagedGridNode mainNode = nodeHost.getMainNode(); if (!mainNode.isActive()) return FluidStack.EMPTY; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java index 6ff71705490..68f3bf3b8de 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MEConfigUtil.java @@ -10,20 +10,11 @@ @UtilityClass public class MEConfigUtil { - private final String TAG_CONFIG_HANDLER = "ConfigHandler"; private final String TAG_GHOST_CIRCUIT = "GhostCircuit"; - private final String TAG_AUTO_PULL = "AutoPull"; private final String TAG_DISTINCT_BUSES = "DistinctBuses"; - - public void writeConfigHandler(CompoundTag tag, GenericStackHandler configHandler) { - tag.put(TAG_CONFIG_HANDLER, configHandler.serializeNBT()); - } - - public void readConfigHandler(CompoundTag tag, GenericStackHandler configHandler) { - if (tag.contains(TAG_CONFIG_HANDLER)) { - configHandler.deserializeNBT(tag.getCompound(TAG_CONFIG_HANDLER)); - } - } + private final String TAG_CONFIG_HANDLER = "ConfigHandler"; + private final String TAG_AUTO_PULL = "AutoPull"; + private final String TAG_MIN_STACK_SIZE = "MinStackSize"; public void writeGhostCircuit(CompoundTag tag, IItemHandlerModifiable circuitInventory) { tag.putByte( @@ -38,6 +29,26 @@ public void readGhostCircuit(CompoundTag tag, IItemHandlerModifiable circuitInve } } + public void writeDistinctBuses(CompoundTag tag, boolean distinctBuses) { + tag.putBoolean(TAG_DISTINCT_BUSES, distinctBuses); + } + + public void readDistinctBuses(CompoundTag tag, Consumer setter) { + if (tag.contains(TAG_DISTINCT_BUSES)) { + setter.accept(tag.getBoolean(TAG_DISTINCT_BUSES)); + } + } + + public void writeConfigHandler(CompoundTag tag, GenericStackHandler configHandler) { + tag.put(TAG_CONFIG_HANDLER, configHandler.serializeNBT()); + } + + public void readConfigHandler(CompoundTag tag, GenericStackHandler configHandler) { + if (tag.contains(TAG_CONFIG_HANDLER)) { + configHandler.deserializeNBT(tag.getCompound(TAG_CONFIG_HANDLER)); + } + } + public void writeAutoPull(CompoundTag tag, boolean autoPull) { tag.putBoolean(TAG_AUTO_PULL, autoPull); } @@ -48,13 +59,13 @@ public void readAutoPull(CompoundTag tag, Consumer setter) { } } - public void writeDistinctBuses(CompoundTag tag, boolean distinctBuses) { - tag.putBoolean(TAG_DISTINCT_BUSES, distinctBuses); + public void writeMinStackSize(CompoundTag tag, int minStackSize) { + tag.putInt(TAG_MIN_STACK_SIZE, minStackSize); } - public void readDistinctBuses(CompoundTag tag, Consumer setter) { - if (tag.contains(TAG_DISTINCT_BUSES)) { - setter.accept(tag.getBoolean(TAG_DISTINCT_BUSES)); + public void readMinStackSize(CompoundTag tag, Consumer setter) { + if (tag.contains(TAG_MIN_STACK_SIZE)) { + setter.accept(tag.getInt(TAG_MIN_STACK_SIZE)); } } } From 065750074109350c95da05d96b1609d4c477202b Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 06:58:32 +0800 Subject: [PATCH 13/21] optimize stocking config handler autoPull --- .../ae2/utils/StockingConfigHandler.java | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java index 30a0f3e5144..f655de95537 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java @@ -7,6 +7,7 @@ import appeng.api.stacks.KeyCounter; import it.unimi.dsi.fastutil.objects.Object2LongMap; import net.minecraft.nbt.CompoundTag; +import org.apache.commons.lang3.mutable.MutableBoolean; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; @@ -17,7 +18,7 @@ public class StockingConfigHandler extends GenericStackHandler implements IStorageWatcherNode { private @UnknownNullability IStackWatcher storageWatcher; - private final Runnable changeListener; + private Runnable changeListener; public StockingConfigHandler(int size, Runnable changeListener) { super(size); @@ -44,19 +45,18 @@ public void setStackInSlot(int slot, @Nullable GenericStack newStack) { super.setStackInSlot(slot, newStack); - // Notify the listener that the configuration has been updated + // Notify listener changeListener.run(); + // Update watcher if (storageWatcher == null) { return; } - // Remove watcher entry for the previous stack if (oldKey != null) { storageWatcher.remove(oldKey); } - // Register watcher entry for the new stack if (newKey != null) { storageWatcher.add(newKey); } @@ -69,17 +69,17 @@ public void deserializeNBT(CompoundTag nbt) { } /** - * Auto-refresh the configuration list by selecting the keys with the highest amounts from the ME system. - * Only keys passing the provided filter are considered. + * Refresh configuration with Top-K keys (by amount) from the ME storage. + * Only keys matching the filter are considered. * - * @param source The ME system to pull keys from - * @param filter A predicate to filter which keys should be included + * @param source ME storage source + * @param filter key filter */ - public void autoPull(KeyCounter source, StackFilter filter) { + public void autoPull(KeyCounter source, Filter filter) { int slots = getSlots(); if (slots == 0) return; - // Top K algorithm: Use a Min-heap to keep the top 'slotCount' entries with the highest amount + // Top-K via PriorityQueue: keep the highest 'slotCount' entries by amount var topEntries = new PriorityQueue<>(Comparator.comparingLong(Object2LongMap.Entry::getLongValue)); for (var entry : source) { @@ -90,17 +90,31 @@ public void autoPull(KeyCounter source, StackFilter filter) { continue; } - topEntries.offer(entry); - if (topEntries.size() > slots) { + assert topEntries.peek() != null; + if (topEntries.size() < slots) { + // If the heap is not full, add the entry + topEntries.offer(entry); + } else if (topEntries.peek().getLongValue() < amount) { + // If the heap is full but the current entry has a higher amount, replace the smallest entry topEntries.poll(); + topEntries.offer(entry); } } - // Fill configuration slots from highest to lowest amount + // Suppress listener; batch updates with a single notification + Runnable original = this.changeListener; + MutableBoolean changed = new MutableBoolean(false); + this.changeListener = changed::setTrue; + // Fill slots from highest to lowest for (int i = slots - 1; !topEntries.isEmpty(); i--) { var entry = topEntries.poll(); setKeyInSlot(i, entry.getKey()); } + // Restore listener and emit once if changed + this.changeListener = original; + if (changed.booleanValue()) { + changeListener.run(); + } } private void syncWatcher() { @@ -114,7 +128,7 @@ private void syncWatcher() { } } - public interface StackFilter { + public interface Filter { boolean test(AEKey key, long amount); } } From 23f1ccf7c2e209306aa30873f05465baf952ffce Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 08:01:10 +0800 Subject: [PATCH 14/21] fix stocking config handler slot padding --- .../gtceu/integration/ae2/utils/StockingConfigHandler.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java index f655de95537..3adefaa854d 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/StockingConfigHandler.java @@ -106,7 +106,12 @@ public void autoPull(KeyCounter source, Filter filter) { MutableBoolean changed = new MutableBoolean(false); this.changeListener = changed::setTrue; // Fill slots from highest to lowest - for (int i = slots - 1; !topEntries.isEmpty(); i--) { + for (int i = slots - 1; i >= 0; i--) { + // Pad with null if no entry available + if (i >= topEntries.size()) { + setKeyInSlot(i, null); + continue; + } var entry = topEntries.poll(); setKeyInSlot(i, entry.getKey()); } From 18ba9fb40f68e012a54cd2659adba3e0f637040c Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 09:22:35 +0800 Subject: [PATCH 15/21] adjust ME part autoIO update checks --- .../integration/ae2/machine/MEInputBusPartMachine.java | 6 +++--- .../integration/ae2/machine/MEInputHatchPartMachine.java | 6 +++--- .../integration/ae2/machine/MEStockingBusPartMachine.java | 6 +++--- .../integration/ae2/machine/MEStockingHatchPartMachine.java | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java index 81185b8ca94..816e58db214 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java @@ -81,12 +81,12 @@ public boolean swapIO() { @Override protected void autoIO() { - IGrid grid = nodeHost.getMainNode().getGrid(); - if (grid == null) return; - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; if (getOffsetTimer() % updateInterval != 0) return; + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; + MEStorage networkInv = grid.getStorageService().getInventory(); NotifiableItemStackHandler inventory = getInventory(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java index 45666bc8852..ac499b7626f 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java @@ -54,12 +54,12 @@ protected boolean shouldUpdateSubscription(Direction newFacing) { @Override protected void autoIO() { - IGrid grid = nodeHost.getMainNode().getGrid(); - if (grid == null) return; - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; if (self().getOffsetTimer() % updateInterval != 0) return; + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; + MEStorage networkInv = grid.getStorageService().getInventory(); FluidTank[] tanks = tank.getStorages(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index 7ea5f786db5..41a052b743f 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -93,12 +93,12 @@ protected boolean shouldUpdateSubscription(Direction newFacing) { @Override public void autoIO() { - IGrid grid = nodeHost.getMainNode().getGrid(); - if (grid == null) return; - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; if (getOffsetTimer() % updateInterval != 0) return; + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; + KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); configHandler.autoPull(cachedInv, (key, amount) -> amount >= minStackSize && key instanceof AEItemKey); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index 7b0674d81ae..c2df1148365 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -89,12 +89,12 @@ protected boolean shouldUpdateSubscription(Direction newFacing) { @Override protected void autoIO() { - IGrid grid = nodeHost.getMainNode().getGrid(); - if (grid == null) return; - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; if (getOffsetTimer() % updateInterval != 0) return; + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; + KeyCounter cachedInv = grid.getStorageService().getCachedInventory(); configHandler.autoPull(cachedInv, (key, amount) -> amount >= minStackSize && key instanceof AEFluidKey); } From d622cceabd0c5b61c1e516c4404328a9aa539380 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 09:51:59 +0800 Subject: [PATCH 16/21] refactor ME part sync tick logic --- .../gtceu/integration/ae2/machine/MEBusPartMachine.java | 5 +++++ .../gtceu/integration/ae2/machine/MEHatchPartMachine.java | 5 +++++ .../gtceu/integration/ae2/machine/MEInputBusPartMachine.java | 4 +--- .../integration/ae2/machine/MEInputHatchPartMachine.java | 4 +--- .../integration/ae2/machine/MEStockingBusPartMachine.java | 4 +--- .../integration/ae2/machine/MEStockingHatchPartMachine.java | 4 +--- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java index 96f7064e6d2..2404d8fd227 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEBusPartMachine.java @@ -45,6 +45,11 @@ protected GridNodeHost createNodeHost() { return host; } + protected boolean isMESyncTick() { + int interval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + return getOffsetTimer() % interval == 0; + } + protected boolean shouldUpdateSubscription(Direction newFacing) { return isWorkingEnabled() && ((io.support(IO.OUT) && !getInventory().isEmpty()) || io.support(IO.IN)) && GTTransferUtils.hasAdjacentItemHandler(getLevel(), getPos(), newFacing); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java index 6dfb65c37c3..cfc417ba558 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEHatchPartMachine.java @@ -43,6 +43,11 @@ protected GridNodeHost createNodeHost() { return host; } + protected boolean isMESyncTick() { + int interval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; + return getOffsetTimer() % interval == 0; + } + protected boolean shouldUpdateSubscription(Direction newFacing) { return isWorkingEnabled() && ((io.support(IO.OUT) && !tank.isEmpty()) || io.support(IO.IN)) && GTTransferUtils.hasAdjacentFluidHandler(getLevel(), getPos(), newFacing); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java index 816e58db214..ec958c61558 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputBusPartMachine.java @@ -12,7 +12,6 @@ import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; @@ -81,8 +80,7 @@ public boolean swapIO() { @Override protected void autoIO() { - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; - if (getOffsetTimer() % updateInterval != 0) return; + if (!isMESyncTick()) return; IGrid grid = nodeHost.getMainNode().getGrid(); if (grid == null) return; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java index ac499b7626f..f5212c4a339 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEInputHatchPartMachine.java @@ -11,7 +11,6 @@ import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; -import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.utils.GenericStackHandler; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; @@ -54,8 +53,7 @@ protected boolean shouldUpdateSubscription(Direction newFacing) { @Override protected void autoIO() { - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; - if (self().getOffsetTimer() % updateInterval != 0) return; + if (!isMESyncTick()) return; IGrid grid = nodeHost.getMainNode().getGrid(); if (grid == null) return; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index 41a052b743f..a379db1efe7 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -17,7 +17,6 @@ import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.gui.fancyconfigurator.StockingFancyConfigurator; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; @@ -93,8 +92,7 @@ protected boolean shouldUpdateSubscription(Direction newFacing) { @Override public void autoIO() { - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; - if (getOffsetTimer() % updateInterval != 0) return; + if (!isMESyncTick()) return; IGrid grid = nodeHost.getMainNode().getGrid(); if (grid == null) return; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index c2df1148365..d051c99328f 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -17,7 +17,6 @@ import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; -import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.gui.fancyconfigurator.StockingFancyConfigurator; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; @@ -89,8 +88,7 @@ protected boolean shouldUpdateSubscription(Direction newFacing) { @Override protected void autoIO() { - int updateInterval = ConfigHolder.INSTANCE.compat.ae2.updateIntervals; - if (getOffsetTimer() % updateInterval != 0) return; + if (!isMESyncTick()) return; IGrid grid = nodeHost.getMainNode().getGrid(); if (grid == null) return; From 4ca8a33b2e58284c763f012634a2c53220d71d76 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 09:54:36 +0800 Subject: [PATCH 17/21] refactor ME output bus/hatch to use KeyStorageBaked --- .../common/data/machines/GTAEMachines.java | 4 +- .../ae2/machine/MEOutputBusPartMachine.java | 231 +++--------------- .../ae2/machine/MEOutputHatchPartMachine.java | 211 +++------------- .../machine/trait/KeyStorageBakedHandler.java | 152 ++++++++++++ .../machine/trait/KeyStorageBakedTank.java | 136 +++++++++++ .../integration/ae2/utils/AEKeyStorage.java | 83 ++++--- 6 files changed, 391 insertions(+), 426 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java index 35a39406d97..a27361f816d 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTAEMachines.java @@ -46,7 +46,7 @@ public class GTAEMachines { .register(); public final static MachineDefinition ITEM_EXPORT_BUS_ME = REGISTRATE - .machine("me_output_bus", MEOutputBusPartMachine::new) + .machine("me_output_bus", be -> new MEOutputBusPartMachine(be, EV)) .langValue("ME Output Bus") .tier(EV) .rotationState(RotationState.ALL) @@ -90,7 +90,7 @@ public class GTAEMachines { .register(); public final static MachineDefinition FLUID_EXPORT_HATCH_ME = REGISTRATE - .machine("me_output_hatch", MEOutputHatchPartMachine::new) + .machine("me_output_hatch", be -> new MEOutputHatchPartMachine(be, EV)) .langValue("ME Output Hatch") .tier(EV) .rotationState(RotationState.ALL) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java index 0270e3f5fb7..8230b33caee 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java @@ -1,35 +1,21 @@ package com.gregtechceu.gtceu.integration.ae2.machine; +import appeng.api.networking.IGrid; import appeng.api.networking.IGridNodeListener; import appeng.api.networking.IManagedGridNode; -import appeng.api.stacks.AEItemKey; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; -import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; -import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBakedHandler; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; -import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import lombok.Setter; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.function.IntFunction; -import java.util.function.Predicate; +import net.minecraft.core.Direction; +import org.apache.commons.lang3.ArrayUtils; public class MEOutputBusPartMachine extends MEBusPartMachine { @@ -39,59 +25,47 @@ public class MEOutputBusPartMachine extends MEBusPartMachine { ); @Persisted - private final AEKeyStorage storage = new AEKeyStorage(); - - public MEOutputBusPartMachine(IMachineBlockEntity holder) { - this(holder, holder.getDefinition().getTier()); - } + protected final AEKeyStorage keyStorage; public MEOutputBusPartMachine(IMachineBlockEntity holder, int tier, Object... args) { - super(holder, tier, IO.OUT); - this.inventory = new InaccessibleInfiniteHandler(this); + super(holder, tier, IO.OUT, args = ArrayUtils.addAll(args, new AEKeyStorage())); + this.keyStorage = (AEKeyStorage) args[args.length - 1]; } @Override - public void onLoad() { - super.onLoad(); - hookInternalBufferListener(); - updateSubscription(); + protected NotifiableItemStackHandler createInventory(Object... args) { + return new KeyStorageBakedHandler(this, (AEKeyStorage) args[args.length - 1]); } @Override - public void onMainNodeStateChanged(IGridNodeListener.State reason) { - IGridConnectedMachine.super.onMainNodeStateChanged(reason); - updateSubscription(); + protected boolean shouldUpdateSubscription(Direction newFacing) { + IManagedGridNode mainNode = nodeHost.getMainNode(); + return isWorkingEnabled() && mainNode.isActive() && !keyStorage.isEmpty(); } @Override - public void setWorkingEnabled(boolean workingEnabled) { - super.setWorkingEnabled(workingEnabled); - updateSubscription(); - } + protected void autoIO() { + if (!isMESyncTick()) return; - @Override - protected void updateSubscription() { - IManagedGridNode node = nodeHost.getMainNode(); - if (isWorkingEnabled() && node.isActive() && !internalBuffer.isEmpty()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - } else if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; + + keyStorage.transferTo(grid.getStorageService().getInventory(), actionSource); } - protected void autoIO() { - var grid = nodeHost.getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) { - internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); - } + @Override + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateInventorySubscription(); } @Override public void onMachineRemoved() { - var grid = nodeHost.getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) - internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); + IManagedGridNode mainNode = nodeHost.getMainNode(); + if (!keyStorage.isEmpty() && mainNode.isActive()) { + assert mainNode.getGrid() != null; + keyStorage.transferTo(mainNode.getGrid().getStorageService().getInventory(), actionSource); + } } @Override @@ -100,7 +74,7 @@ public Widget createUIWidget() { group.addWidget(new LabelWidget(5, 0, () -> nodeHost.getMainNode().isActive() ? "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); group.addWidget(new LabelWidget(5, 10, "gtceu.gui.waiting_list")); - group.addWidget(new AEListGridWidget.Item(5, 20, 3, this.internalBuffer)); + group.addWidget(new AEListGridWidget.Item(5, 20, 3, this.keyStorage)); return group; } @@ -109,153 +83,4 @@ public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - private void hookInternalBufferListener() { - if (internalBufferListenerHooked) return; - internalBufferListenerHooked = true; - - Runnable previous = internalBuffer.getOnContentsChanged(); - internalBuffer.setOnContentsChanged(() -> { - if (previous != null) previous.run(); - inventory.onContentsChanged(); - updateSubscription(); - }); - } - - static class Adapter extends NotifiableItemStackHandler { - - private static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - Adapter.class, - NotifiableItemStackHandler.MANAGED_FIELD_HOLDER - ); - - private final AEKeyStorage storage; - @Setter - private Predicate filter = stack -> true; - - public Adapter(MetaMachine machine, AEKeyStorage storage) { - super(machine, 0, IO.OUT, IO.NONE); - this.storage = storage; - this.storage.setOnContentsChanged(this::onContentsChanged); - } - - @Override - public List handleRecipeInner(IO io, GTRecipe recipe, List left, boolean simulate) { - if (io != handlerIO) return left; - if (io != IO.IN && io != IO.OUT) return left.isEmpty() ? null : left; - - // Temporarily remove listener so that we can broadcast the entire set of transactions once - Runnable listener = storage.getOnContentsChanged(); - storage.setOnContentsChanged(() -> {}); - boolean changed = false; - - // Store the ItemStack in each slot after an operation - // Necessary for simulation since we don't actually modify the slot's contents - // Doesn't hurt for execution, and definitely cheaper than copying the entire storage - ItemStack[] visited = new ItemStack[storage.getSlots()]; - for (var it = left.listIterator(); it.hasNext();) { - var ingredient = it.next(); - if (ingredient.isEmpty()) { - it.remove(); - continue; - } - - ItemStack[] items; - int amount; - if (ingredient instanceof IntProviderIngredient provider) { - provider.setItemStacks(null); - provider.setSampledCount(-1); - - ItemStack output; - if (simulate) { - output = provider.getMaxSizeStack(); - items = new ItemStack[] { output }; - } else { - items = provider.getItems(); - if (items.length == 0 || items[0].isEmpty()) { - it.remove(); - continue; - } - output = items[0]; - } - amount = output.getCount(); - } else { - items = ingredient.getItems(); - if (items.length == 0 || items[0].isEmpty()) { - it.remove(); - continue; - } - if (ingredient instanceof SizedIngredient si) amount = si.getAmount(); - else amount = items[0].getCount(); - } - - for (int slot = 0; slot < storage.getSlots(); ++slot) { - ItemStack current = visited[slot] == null ? storage.getStackInSlot(slot) : visited[slot]; - int count = current.getCount(); - - ItemStack output = items[0].copyWithCount(amount); - // Only try this slot if not visited or if visited with the same type of item - if (visited[slot] == null || GTUtil.isSameItemSameTags(visited[slot], output)) { - if (count < output.getMaxStackSize() && count < storage.getSlotLimit(slot)) { - var remainder = getActioned(storage, slot, recipe.ingredientActions); - if (remainder == null) remainder = storage.insertItem(slot, output, simulate); - if (remainder.getCount() < amount) { - changed = true; - visited[slot] = output.copyWithCount(count + amount - remainder.getCount()); - } - amount = remainder.getCount(); - } - } - - if (amount <= 0) { - it.remove(); - break; - } - } - // Modify ingredient if we didn't finish it off - if (amount > 0) { - if (ingredient instanceof SizedIngredient si) { - si.setAmount(amount); - } else { - items[0].setCount(amount); - } - } - } - - storage.setOnContentsChanged(listener); - if (changed && !simulate) listener.run(); - - return left.isEmpty() ? null : left; - } - - @Override - public List getContents() { - return storage.stream() - .map(entry -> { - AEItemKey key = (AEItemKey) entry.getKey(); - int amount = entry.getIntValue(); - return key.toStack(amount); - }) - .toList(); - } - - @Override - public double getTotalContentAmount() { - return storage.values().intStream().sum(); - } - - @Override - public int getSize() { - return storage.size(); - } - - @Override - public boolean isEmpty() { - return storage.isEmpty(); - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java index 5680fd82d00..c4f8ecc3f24 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java @@ -1,32 +1,21 @@ package com.gregtechceu.gtceu.integration.ae2.machine; +import appeng.api.networking.IGrid; import appeng.api.networking.IGridNodeListener; import appeng.api.networking.IManagedGridNode; -import appeng.api.stacks.AEFluidKey; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.TickableSubscription; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; -import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBakedTank; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; -import com.gregtechceu.gtceu.utils.GTMath; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; -import lombok.Getter; -import net.minecraftforge.fluids.FluidStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; +import net.minecraft.core.Direction; +import org.apache.commons.lang3.ArrayUtils; public class MEOutputHatchPartMachine extends MEHatchPartMachine { @@ -36,66 +25,47 @@ public class MEOutputHatchPartMachine extends MEHatchPartMachine { ); @Persisted - private AEKeyStorage internalBuffer; - @Getter - @Persisted - private final NotifiableFluidTank tank; - @Nullable - protected TickableSubscription autoIOSubs; - private boolean internalBufferListenerHooked; - - public MEOutputHatchPartMachine(IMachineBlockEntity holder) { - this(holder, holder.getDefinition().getTier(), 0, 0); - } + protected final AEKeyStorage keyStorage; - public MEOutputHatchPartMachine(IMachineBlockEntity holder, int tier, int initialTankCapacity, int slots, Object... args) { - super(holder, tier, IO.OUT); - this.internalBuffer = new AEKeyStorage(); - this.tank = new InaccessibleInfiniteTank(this); + public MEOutputHatchPartMachine(IMachineBlockEntity holder, int tier, Object... args) { + super(holder, tier, IO.OUT, 0, 0, args = ArrayUtils.addAll(args, new AEKeyStorage())); + this.keyStorage = (AEKeyStorage) args[args.length - 1]; } @Override - public void onLoad() { - super.onLoad(); - hookInternalBufferListener(); - updateSubscription(); + protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object... args) { + return new KeyStorageBakedTank(this, (AEKeyStorage) args[args.length - 1]); } @Override - public void onMainNodeStateChanged(IGridNodeListener.State reason) { - IGridConnectedMachine.super.onMainNodeStateChanged(reason); - updateSubscription(); + protected boolean shouldUpdateSubscription(Direction newFacing) { + IManagedGridNode mainNode = nodeHost.getMainNode(); + return isWorkingEnabled() && mainNode.isActive() && !keyStorage.isEmpty(); } @Override - public void setWorkingEnabled(boolean workingEnabled) { - super.setWorkingEnabled(workingEnabled); - updateSubscription(); - } + protected void autoIO() { + if (!isMESyncTick()) return; - @Override - protected void updateSubscription() { - IManagedGridNode node = nodeHost.getMainNode(); - if (isWorkingEnabled() && node.isActive() && !internalBuffer.isEmpty()) { - autoIOSubs = subscribeServerTick(autoIOSubs, this::autoIO); - } else if (autoIOSubs != null) { - autoIOSubs.unsubscribe(); - autoIOSubs = null; - } + IGrid grid = nodeHost.getMainNode().getGrid(); + if (grid == null) return; + + keyStorage.transferTo(grid.getStorageService().getInventory(), actionSource); } - protected void autoIO() { - var grid = nodeHost.getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) { - internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); - } + @Override + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateTankSubscription(); } @Override public void onMachineRemoved() { - var grid = nodeHost.getMainNode().getGrid(); - if (grid != null && !internalBuffer.isEmpty()) - internalBuffer.transferTo(grid.getStorageService().getInventory(), actionSource); + IManagedGridNode mainNode = nodeHost.getMainNode(); + if (!keyStorage.isEmpty() && mainNode.isActive()) { + assert mainNode.getGrid() != null; + keyStorage.transferTo(mainNode.getGrid().getStorageService().getInventory(), actionSource); + } } @Override @@ -104,7 +74,7 @@ public Widget createUIWidget() { group.addWidget(new LabelWidget(5, 0, () -> nodeHost.getMainNode().isActive() ? "gtceu.gui.me_network.online" : "gtceu.gui.me_network.offline")); group.addWidget(new LabelWidget(5, 10, "gtceu.gui.waiting_list")); - group.addWidget(new AEListGridWidget.Fluid(5, 20, 3, this.internalBuffer)); + group.addWidget(new AEListGridWidget.Fluid(5, 20, 3, this.keyStorage)); return group; } @@ -113,127 +83,4 @@ public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - private void hookInternalBufferListener() { - if (internalBufferListenerHooked) return; - internalBufferListenerHooked = true; - - Runnable previous = internalBuffer.getOnContentsChanged(); - internalBuffer.setOnContentsChanged(() -> { - if (previous != null) previous.run(); - tank.onContentsChanged(); - updateSubscription(); - }); - } - - private class InaccessibleInfiniteTank extends NotifiableFluidTank { - - private final FluidStorageDelegate storage; - - public InaccessibleInfiniteTank(MetaMachine holder) { - super(holder, List.of(new FluidStorageDelegate()), IO.OUT, IO.NONE); - storage = (FluidStorageDelegate) getStorages()[0]; - allowSameFluids = true; - } - - @Override - public int getTanks() { - return 1; - } - - @Override - public @NotNull List getContents() { - return Collections.emptyList(); - } - - @Override - public double getTotalContentAmount() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public @NotNull FluidStack getFluidInTank(int tank) { - return FluidStack.EMPTY; - } - - @Override - public void setFluidInTank(int tank, @NotNull FluidStack fluidStack) {} - - @Override - public int getTankCapacity(int tank) { - return storage.getCapacity(); - } - - @Override - public boolean isFluidValid(int tank, @NotNull FluidStack stack) { - return true; - } - - @Override - @Nullable - public List handleRecipeInner(IO io, GTRecipe recipe, List left, - boolean simulate) { - if (io != IO.OUT) return left; - FluidAction action = simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE; - for (var it = left.iterator(); it.hasNext();) { - var ingredient = it.next(); - if (ingredient.isEmpty()) { - it.remove(); - continue; - } - var fluids = ingredient.getStacks(); - if (fluids.length == 0 || fluids[0].isEmpty()) { - it.remove(); - continue; - } - FluidStack output = fluids[0]; - ingredient.shrink(storage.fill(output, action)); - if (ingredient.getAmount() <= 0) it.remove(); - } - return left.isEmpty() ? null : left; - } - } - - private class FluidStorageDelegate extends CustomFluidTank { - - public FluidStorageDelegate() { - super(0); - } - - @Override - public int getCapacity() { - return Integer.MAX_VALUE; - } - - @Override - public void setFluid(FluidStack fluid) {} - - @Override - public int fill(FluidStack resource, FluidAction action) { - if (resource.isEmpty() || resource.getAmount() <= 0) return 0; - var key = AEFluidKey.of(resource.getFluid(), resource.getTag()); - int amount = resource.getAmount(); - int oldValue = GTMath.saturatedCast(internalBuffer.storage.getOrDefault(key, 0)); - int changeValue = Math.min(Integer.MAX_VALUE - oldValue, amount); - if (changeValue > 0 && action.execute()) { - internalBuffer.storage.put(key, oldValue + changeValue); - internalBuffer.onChanged(); - } - return changeValue; - } - - @Override - public boolean supportsFill(int tank) { - return false; - } - - @Override - public boolean supportsDrain(int tank) { - return false; - } - } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java new file mode 100644 index 00000000000..c69f818971d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java @@ -0,0 +1,152 @@ +package com.gregtechceu.gtceu.integration.ae2.machine.trait; + +import appeng.api.stacks.AEItemKey; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; +import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; +import com.gregtechceu.gtceu.utils.GTMath; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +public class KeyStorageBakedHandler extends NotifiableItemStackHandler { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + KeyStorageBakedHandler.class, + NotifiableItemStackHandler.MANAGED_FIELD_HOLDER + ); + + protected final AEKeyStorage keyStorage; + + public KeyStorageBakedHandler(MetaMachine machine, AEKeyStorage keyStorage) { + super(machine, 0, IO.OUT, IO.NONE); + this.keyStorage = keyStorage; + keyStorage.setOnContentsChanged(this::onContentsChanged); + } + + public static @Nullable List handleRecipe( + IO io, + GTRecipe recipe, + List left, + boolean simulate, + IO handlerIO, + AEKeyStorage storage + ) { + // Only handle the intended IO type + if (io != handlerIO || (io != IO.IN && io != IO.OUT)) { + return left.isEmpty() ? null : left; + } + + // Temporarily suppress keyStorage listener to batch notifications + Runnable originalListener = storage.getOnContentsChanged(); + MutableBoolean changed = new MutableBoolean(false); + storage.setOnContentsChanged(changed::setTrue); + + ListIterator it = left.listIterator(); + while (it.hasNext()) { + Ingredient ingredient = it.next(); + + // Remove empty ingredients + if (ingredient.isEmpty()) { + it.remove(); + continue; + } + + ItemStack output; + int amount; + // Handle IntProviderIngredient separately + if (ingredient instanceof IntProviderIngredient provider) { + provider.setItemStacks(null); + provider.setSampledCount(-1); + output = simulate ? provider.getMaxSizeStack() : getFirstStack(provider.getItems()); + amount = output.getCount(); + } else { + output = getFirstStack(ingredient.getItems()); + // Preserve the ingredient amount if it's sized + if (ingredient instanceof SizedIngredient si) { + amount = si.getAmount(); + } else { + amount = output.getCount(); + } + } + + // Insert into keyStorage and compute remaining amount + AEItemKey key = AEItemKey.of(output); + if (key == null) continue; + + int inserted = Math.toIntExact(storage.add(key, amount, simulate)); + int remaining = amount - inserted; + + if (remaining <= 0) { + it.remove(); + continue; + } + + // Update ingredient with remaining amount + if (ingredient instanceof SizedIngredient si) { + si.setAmount(remaining); + } else { + output.setCount(remaining); + } + } + + // Restore listener and trigger if changes occurred and not simulating + storage.setOnContentsChanged(originalListener); + if (changed.booleanValue() && !simulate) { + originalListener.run(); + } + + return left.isEmpty() ? null : left; + } + + private static ItemStack getFirstStack(ItemStack[] items) { + return (items.length == 0) ? ItemStack.EMPTY : items[0]; + } + + @Override + public @Nullable List handleRecipeInner(IO io, GTRecipe recipe, List left, boolean simulate) { + return handleRecipe(io, recipe, left, simulate, IO.OUT, keyStorage); + } + + @Override + public List getContents() { + List result = new ArrayList<>(keyStorage.size()); + for (var entry : keyStorage) { + AEItemKey key = (AEItemKey) entry.getKey(); + long amount = entry.getLongValue(); + result.addAll(GTMath.splitStacks(key.toStack(), amount)); + } + return result; + } + + @Override + public double getTotalContentAmount() { + return keyStorage.stream().mapToLong(Object2LongMap.Entry::getLongValue).sum(); + } + + @Override + public int getSize() { + return keyStorage.size(); + } + + @Override + public boolean isEmpty() { + return keyStorage.isEmpty(); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java new file mode 100644 index 00000000000..504af8cd081 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java @@ -0,0 +1,136 @@ +package com.gregtechceu.gtceu.integration.ae2.machine.trait; + +import appeng.api.stacks.AEFluidKey; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; +import com.gregtechceu.gtceu.api.recipe.ingredient.IntProviderFluidIngredient; +import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; +import com.gregtechceu.gtceu.utils.GTMath; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import net.minecraftforge.fluids.FluidStack; +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +public class KeyStorageBakedTank extends NotifiableFluidTank { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + KeyStorageBakedTank.class, + NotifiableFluidTank.MANAGED_FIELD_HOLDER + ); + + protected final AEKeyStorage keyStorage; + + public KeyStorageBakedTank(MetaMachine machine, AEKeyStorage keyStorage) { + super(machine, 0, 0, IO.OUT, IO.NONE); + this.keyStorage = keyStorage; + keyStorage.setOnContentsChanged(this::onContentsChanged); + } + + public static @Nullable List handleRecipe( + IO io, + GTRecipe recipe, + List left, + boolean simulate, + IO handlerIO, + AEKeyStorage storage + ) { + if (io != handlerIO || (io != IO.IN && io != IO.OUT)) { + return left.isEmpty() ? null : left; + } + + Runnable originalListener = storage.getOnContentsChanged(); + MutableBoolean changed = new MutableBoolean(false); + storage.setOnContentsChanged(changed::setTrue); + + ListIterator it = left.listIterator(); + while (it.hasNext()) { + FluidIngredient ingredient = it.next(); + + if (ingredient.isEmpty()) { + it.remove(); + continue; + } + + FluidStack output; + int amount; + if (ingredient instanceof IntProviderFluidIngredient provider) { + provider.setFluidStacks(null); + provider.setSampledCount(-1); + output = simulate ? provider.getMaxSizeStack() : getFirstStack(provider.getStacks()); + } else { + output = getFirstStack(ingredient.getStacks()); + } + + // Insert into keyStorage and compute remaining amount + AEFluidKey key = AEFluidKey.of(output); + if (key == null) continue; + amount = output.getAmount(); + + int inserted = Math.toIntExact(storage.add(key, amount, simulate)); + int remaining = amount - inserted; + + if (remaining <= 0) { + it.remove(); + continue; + } + + ingredient.setAmount(remaining); + } + + storage.setOnContentsChanged(originalListener); + if (changed.booleanValue() && !simulate) { + originalListener.run(); + } + + return left.isEmpty() ? null : left; + } + + private static FluidStack getFirstStack(FluidStack[] stacks) { + return (stacks.length == 0) ? FluidStack.EMPTY : stacks[0]; + } + + @Override + public @Nullable List handleRecipeInner(IO io, GTRecipe recipe, List left, + boolean simulate) { + return handleRecipe(io, recipe, left, simulate, IO.OUT, keyStorage); + } + + @Override + public List getContents() { + List result = new ArrayList<>(keyStorage.size()); + for (var entry : keyStorage) { + AEFluidKey key = (AEFluidKey) entry.getKey(); + long amount = entry.getLongValue(); + result.addAll(GTMath.splitFluidStacks(key.toStack(1), amount)); + } + return result; + } + + @Override + public double getTotalContentAmount() { + return keyStorage.stream().mapToLong(Object2LongMap.Entry::getLongValue).sum(); + } + + @Override + public int getSize() { + return keyStorage.size(); + } + + @Override + public boolean isEmpty() { + return keyStorage.isEmpty(); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java index 7c9f6a59848..f0b41f57281 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java @@ -6,10 +6,8 @@ import appeng.api.storage.MEStorage; import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; -import it.unimi.dsi.fastutil.ints.IntCollection; -import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import lombok.Getter; import lombok.Setter; import net.minecraft.nbt.CompoundTag; @@ -22,29 +20,47 @@ * Storage for AE2 keys with associated amounts. * Supports serialization, change tracking, and inventory operations. */ -public class AEKeyStorage implements Iterable>, ITagSerializable, IContentChangeAware { +public class AEKeyStorage implements Iterable>, ITagSerializable, IContentChangeAware { - private final Object2IntMap storage = new Object2IntLinkedOpenHashMap<>(); + private final Object2LongMap storage = new Object2LongOpenHashMap<>(); @Getter @Setter private Runnable onContentsChanged = () -> { }; - public void add(AEKey key, int amount) { - if (amount <= 0) return; - storage.mergeInt(key, amount, Integer::sum); - onContentsChanged.run(); + /** + * Adds a specified amount of an {@link AEKey} to the storage. + * The stored amount will not exceed {@link Long#MAX_VALUE}. + * + * @param key The key to add. + * @param amount The quantity to add (ignored if <= 0). + * @return The actual amount added to the storage. + */ + public long add(AEKey key, int amount, boolean simulate) { + if (amount <= 0) return 0; + + long existing = storage.getLong(key); + long inserted = Math.min(amount, Long.MAX_VALUE - existing); + if (inserted > 0 && !simulate) { + storage.put(key, existing + inserted); + // Notify that the storage contents have changed + onContentsChanged.run(); + } + return inserted; } - public int get(AEKey key) { - return storage.getInt(key); + public long get(AEKey key) { + return storage.getLong(key); } - public void remove(AEKey key) { - if (storage.removeInt(key) != 0) { + public long remove(AEKey key, boolean simulate) { + long amount = storage.getLong(key); + if (amount != 0 && !simulate) { + storage.removeLong(key); onContentsChanged.run(); } + return amount; } public void clear() { @@ -62,16 +78,13 @@ public int size() { return storage.size(); } - public ObjectSet keySet() { - return storage.keySet(); - } - - public IntCollection values() { - return storage.values(); + public Stream> stream() { + return storage.object2LongEntrySet().stream(); } - public Stream> stream() { - return storage.object2IntEntrySet().stream(); + @Override + public Iterator> iterator() { + return storage.object2LongEntrySet().iterator(); } /** @@ -81,17 +94,16 @@ public Stream> stream() { public void transferTo(MEStorage inventory, IActionSource source) { if (storage.isEmpty()) return; - var it = storage.object2IntEntrySet().iterator(); boolean changed = false; - while (it.hasNext()) { + for (var it = iterator(); it.hasNext(); ) { var entry = it.next(); - int transferred = Math.toIntExact( - inventory.insert(entry.getKey(), entry.getIntValue(), Actionable.MODULATE, source) - ); - if (transferred > 0) { + AEKey key = entry.getKey(); + long amount = entry.getLongValue(); + long inserted = inventory.insert(key, amount, Actionable.MODULATE, source); + if (inserted > 0) { changed = true; - int remaining = entry.getIntValue() - transferred; + long remaining = amount - inserted; if (remaining <= 0) { it.remove(); } else { @@ -100,23 +112,16 @@ public void transferTo(MEStorage inventory, IActionSource source) { } } - if (changed) { - onContentsChanged.run(); - } - } - - @Override - public Iterator> iterator() { - return storage.object2IntEntrySet().iterator(); + if (changed) onContentsChanged.run(); } @Override public ListTag serializeNBT() { var list = new ListTag(); - for (var entry : storage.object2IntEntrySet()) { + for (var entry : storage.object2LongEntrySet()) { var tag = new CompoundTag(); tag.put("key", entry.getKey().toTagGeneric()); - tag.putInt("amount", entry.getIntValue()); + tag.putLong("amount", entry.getLongValue()); list.add(tag); } return list; From e11aa1bb7dfc04026bc9efe33857dc58cbda603a Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 18:44:24 +0800 Subject: [PATCH 18/21] fix typo --- .../integration/ae2/machine/MEOutputBusPartMachine.java | 4 ++-- .../integration/ae2/machine/MEOutputHatchPartMachine.java | 4 ++-- ...torageBakedHandler.java => KeyStorageBackedHandler.java} | 6 +++--- .../{KeyStorageBakedTank.java => KeyStorageBackedTank.java} | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/{KeyStorageBakedHandler.java => KeyStorageBackedHandler.java} (96%) rename src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/{KeyStorageBakedTank.java => KeyStorageBackedTank.java} (96%) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java index 8230b33caee..5441bca15ba 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java @@ -7,7 +7,7 @@ import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBakedHandler; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBackedHandler; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; @@ -34,7 +34,7 @@ public MEOutputBusPartMachine(IMachineBlockEntity holder, int tier, Object... ar @Override protected NotifiableItemStackHandler createInventory(Object... args) { - return new KeyStorageBakedHandler(this, (AEKeyStorage) args[args.length - 1]); + return new KeyStorageBackedHandler(this, (AEKeyStorage) args[args.length - 1]); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java index c4f8ecc3f24..79d9006a6aa 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java @@ -7,7 +7,7 @@ import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; -import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBakedTank; +import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBackedTank; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; @@ -34,7 +34,7 @@ public MEOutputHatchPartMachine(IMachineBlockEntity holder, int tier, Object... @Override protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object... args) { - return new KeyStorageBakedTank(this, (AEKeyStorage) args[args.length - 1]); + return new KeyStorageBackedTank(this, (AEKeyStorage) args[args.length - 1]); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBackedHandler.java similarity index 96% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBackedHandler.java index c69f818971d..4dfb47591b4 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBackedHandler.java @@ -20,16 +20,16 @@ import java.util.List; import java.util.ListIterator; -public class KeyStorageBakedHandler extends NotifiableItemStackHandler { +public class KeyStorageBackedHandler extends NotifiableItemStackHandler { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - KeyStorageBakedHandler.class, + KeyStorageBackedHandler.class, NotifiableItemStackHandler.MANAGED_FIELD_HOLDER ); protected final AEKeyStorage keyStorage; - public KeyStorageBakedHandler(MetaMachine machine, AEKeyStorage keyStorage) { + public KeyStorageBackedHandler(MetaMachine machine, AEKeyStorage keyStorage) { super(machine, 0, IO.OUT, IO.NONE); this.keyStorage = keyStorage; keyStorage.setOnContentsChanged(this::onContentsChanged); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBackedTank.java similarity index 96% rename from src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java rename to src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBackedTank.java index 504af8cd081..88af217c6c9 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBakedTank.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/KeyStorageBackedTank.java @@ -19,16 +19,16 @@ import java.util.List; import java.util.ListIterator; -public class KeyStorageBakedTank extends NotifiableFluidTank { +public class KeyStorageBackedTank extends NotifiableFluidTank { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( - KeyStorageBakedTank.class, + KeyStorageBackedTank.class, NotifiableFluidTank.MANAGED_FIELD_HOLDER ); protected final AEKeyStorage keyStorage; - public KeyStorageBakedTank(MetaMachine machine, AEKeyStorage keyStorage) { + public KeyStorageBackedTank(MetaMachine machine, AEKeyStorage keyStorage) { super(machine, 0, 0, IO.OUT, IO.NONE); this.keyStorage = keyStorage; keyStorage.setOnContentsChanged(this::onContentsChanged); From af2acd984c602339941b49d8f9fd96190d6dbe89 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 22:08:04 +0800 Subject: [PATCH 19/21] refactor AEKeyStorage transfer logic to AEUtil --- .../ae2/machine/MEOutputBusPartMachine.java | 18 ++++- .../ae2/machine/MEOutputHatchPartMachine.java | 7 +- .../ae2/machine/MEStockingBusPartMachine.java | 21 ++++-- .../machine/MEStockingHatchPartMachine.java | 12 ++-- .../integration/ae2/utils/AEKeyStorage.java | 31 -------- .../gtceu/integration/ae2/utils/AEUtil.java | 70 +++++++++++++++++++ 6 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java index 5441bca15ba..f91cc226380 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputBusPartMachine.java @@ -3,18 +3,22 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridNodeListener; import appeng.api.networking.IManagedGridNode; +import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; +import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBackedHandler; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; +import com.gregtechceu.gtceu.integration.ae2.utils.AEUtil; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; import org.apache.commons.lang3.ArrayUtils; public class MEOutputBusPartMachine extends MEBusPartMachine { @@ -50,7 +54,7 @@ protected void autoIO() { IGrid grid = nodeHost.getMainNode().getGrid(); if (grid == null) return; - keyStorage.transferTo(grid.getStorageService().getInventory(), actionSource); + AEUtil.transferTo(keyStorage, grid.getStorageService().getInventory(), actionSource, true); } @Override @@ -64,7 +68,17 @@ public void onMachineRemoved() { IManagedGridNode mainNode = nodeHost.getMainNode(); if (!keyStorage.isEmpty() && mainNode.isActive()) { assert mainNode.getGrid() != null; - keyStorage.transferTo(mainNode.getGrid().getStorageService().getInventory(), actionSource); + MEStorage networkInv = mainNode.getGrid().getStorageService().getInventory(); + AEUtil.transferTo(keyStorage, networkInv, actionSource, false); + } + + Level level = getLevel(); + if (level != null && !level.isClientSide) { + AEUtil.dropAllItems(level, getPos(), keyStorage); + } + + if (!ConfigHolder.INSTANCE.machines.ghostCircuit) { + clearInventory(circuitInventory.storage); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java index 79d9006a6aa..edab2654a05 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java @@ -3,12 +3,14 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridNodeListener; import appeng.api.networking.IManagedGridNode; +import appeng.api.storage.MEStorage; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBackedTank; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; +import com.gregtechceu.gtceu.integration.ae2.utils.AEUtil; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -50,7 +52,7 @@ protected void autoIO() { IGrid grid = nodeHost.getMainNode().getGrid(); if (grid == null) return; - keyStorage.transferTo(grid.getStorageService().getInventory(), actionSource); + AEUtil.transferTo(keyStorage, grid.getStorageService().getInventory(), actionSource, true); } @Override @@ -64,7 +66,8 @@ public void onMachineRemoved() { IManagedGridNode mainNode = nodeHost.getMainNode(); if (!keyStorage.isEmpty() && mainNode.isActive()) { assert mainNode.getGrid() != null; - keyStorage.transferTo(mainNode.getGrid().getStorageService().getInventory(), actionSource); + MEStorage networkInv = mainNode.getGrid().getStorageService().getInventory(); + AEUtil.transferTo(keyStorage, networkInv, actionSource, false); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java index a379db1efe7..f6ff5166e7e 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingBusPartMachine.java @@ -17,6 +17,7 @@ import com.gregtechceu.gtceu.api.machine.feature.IDataStickConfigurable; import com.gregtechceu.gtceu.api.machine.trait.NotifiableItemStackHandler; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.gui.fancyconfigurator.StockingFancyConfigurator; import com.gregtechceu.gtceu.integration.ae2.utils.MEConfigUtil; import com.gregtechceu.gtceu.integration.ae2.utils.StockingConfigHandler; @@ -78,12 +79,6 @@ protected NotifiableItemStackHandler createInventory(Object... args) { ); } - @Override - public void onMainNodeStateChanged(IGridNodeListener.State reason) { - super.onMainNodeStateChanged(reason); - updateInventorySubscription(); - } - @Override protected boolean shouldUpdateSubscription(Direction newFacing) { IManagedGridNode node = nodeHost.getMainNode(); @@ -101,6 +96,20 @@ public void autoIO() { configHandler.autoPull(cachedInv, (key, amount) -> amount >= minStackSize && key instanceof AEItemKey); } + @Override + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateInventorySubscription(); + } + + @Override + public void onMachineRemoved() { + // nothing to drop + if (!ConfigHolder.INSTANCE.machines.ghostCircuit) { + clearInventory(circuitInventory.storage); + } + } + @Override protected int getInventorySize() { return slots; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java index d051c99328f..7a2bf72711c 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEStockingHatchPartMachine.java @@ -74,12 +74,6 @@ protected NotifiableFluidTank createTank(int initialCapacity, int slots, Object. return new NotifiableFluidTank(this, storages, IO.IN, IO.NONE); } - @Override - public void onMainNodeStateChanged(IGridNodeListener.State reason) { - super.onMainNodeStateChanged(reason); - updateTankSubscription(); - } - @Override protected boolean shouldUpdateSubscription(Direction newFacing) { IManagedGridNode node = nodeHost.getMainNode(); @@ -97,6 +91,12 @@ protected void autoIO() { configHandler.autoPull(cachedInv, (key, amount) -> amount >= minStackSize && key instanceof AEFluidKey); } + @Override + public void onMainNodeStateChanged(IGridNodeListener.State reason) { + super.onMainNodeStateChanged(reason); + updateTankSubscription(); + } + public void setAutoPull(boolean autoPull) { this.autoPull = autoPull; updateTankSubscription(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java index f0b41f57281..09ed28eeea4 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEKeyStorage.java @@ -1,9 +1,6 @@ package com.gregtechceu.gtceu.integration.ae2.utils; -import appeng.api.config.Actionable; -import appeng.api.networking.security.IActionSource; import appeng.api.stacks.AEKey; -import appeng.api.storage.MEStorage; import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; import it.unimi.dsi.fastutil.objects.Object2LongMap; @@ -87,34 +84,6 @@ public Iterator> iterator() { return storage.object2LongEntrySet().iterator(); } - /** - * Transfers stored stacks into the given inventory. - * Removes fully transferred entries and updates partial transfers. - */ - public void transferTo(MEStorage inventory, IActionSource source) { - if (storage.isEmpty()) return; - - boolean changed = false; - - for (var it = iterator(); it.hasNext(); ) { - var entry = it.next(); - AEKey key = entry.getKey(); - long amount = entry.getLongValue(); - long inserted = inventory.insert(key, amount, Actionable.MODULATE, source); - if (inserted > 0) { - changed = true; - long remaining = amount - inserted; - if (remaining <= 0) { - it.remove(); - } else { - entry.setValue(remaining); - } - } - } - - if (changed) onContentsChanged.run(); - } - @Override public ListTag serializeNBT() { var list = new ListTag(); diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java new file mode 100644 index 00000000000..f215abfe3bb --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/AEUtil.java @@ -0,0 +1,70 @@ +package com.gregtechceu.gtceu.integration.ae2.utils; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.storage.MEStorage; +import com.gregtechceu.gtceu.utils.GTMath; +import lombok.experimental.UtilityClass; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; + +@UtilityClass +public class AEUtil { + + /** + * Transfers AEKeyStorage entries to a MEStorage. + * + * @param from The {@link AEKeyStorage} to transfer from. + * @param to The {@link MEStorage} to transfer to. + * @param source The action source for the transfer. + * @param notify Whether to notify the {@link AEKeyStorage} of changes. + */ + public void transferTo(AEKeyStorage from, MEStorage to, IActionSource source, boolean notify) { + if (from.isEmpty()) return; + + boolean changed = false; + + for (var it = from.iterator(); it.hasNext(); ) { + var entry = it.next(); + AEKey key = entry.getKey(); + long amount = entry.getLongValue(); + long inserted = to.insert(key, amount, Actionable.MODULATE, source); + if (inserted > 0) { + changed = true; + long remaining = amount - inserted; + if (remaining <= 0) { + it.remove(); + } else { + entry.setValue(remaining); + } + } + } + + if (changed && notify) { + from.getOnContentsChanged().run(); + } + } + + public void dropAllItems(Level level, BlockPos pos, AEKeyStorage storage) { + for (var iterator = storage.iterator(); iterator.hasNext(); ) { + var entry = iterator.next(); + var key = entry.getKey(); + + if (!(key instanceof AEItemKey itemKey)) { + continue; + } + + long count = entry.getLongValue(); + var stacks = GTMath.splitStacks(itemKey.toStack(), count); + + for (var stack : stacks) { + Block.popResource(level, pos, stack); + } + + iterator.remove(); + } + } +} From 5872dee85fdcc95c5878fb4bb8af7912e59e3acc Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 22:09:54 +0800 Subject: [PATCH 20/21] clear circuit inventory when ghost circuit disabled --- .../integration/ae2/machine/MEOutputHatchPartMachine.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java index edab2654a05..8ffd40c01ec 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/MEOutputHatchPartMachine.java @@ -7,6 +7,7 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.gui.list.AEListGridWidget; import com.gregtechceu.gtceu.integration.ae2.machine.trait.KeyStorageBackedTank; import com.gregtechceu.gtceu.integration.ae2.utils.AEKeyStorage; @@ -69,6 +70,10 @@ public void onMachineRemoved() { MEStorage networkInv = mainNode.getGrid().getStorageService().getInventory(); AEUtil.transferTo(keyStorage, networkInv, actionSource, false); } + + if (!ConfigHolder.INSTANCE.machines.ghostCircuit) { + clearInventory(circuitInventory.storage); + } } @Override From ac9282f2e15bd301276eaef8d5232fa1ae78eda7 Mon Sep 17 00:00:00 2001 From: Gate Guardian Date: Fri, 27 Mar 2026 22:18:09 +0800 Subject: [PATCH 21/21] inline GridNodeHost listener --- .../ae2/machine/trait/GridNodeHost.java | 24 ++++++++++++++----- .../ae2/utils/MachineNodeListener.java | 23 ------------------ 2 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java index 9afd4fea0d4..e7b978150f3 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHost.java @@ -1,14 +1,11 @@ package com.gregtechceu.gtceu.integration.ae2.machine.trait; -import appeng.api.networking.GridHelper; -import appeng.api.networking.IGridNode; -import appeng.api.networking.IInWorldGridNodeHost; -import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.*; import appeng.api.util.AECableType; import appeng.me.InWorldGridNode; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; -import com.gregtechceu.gtceu.integration.ae2.utils.MachineNodeListener; +import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; import lombok.Getter; import lombok.Setter; @@ -22,6 +19,20 @@ public class GridNodeHost extends MachineTrait implements IInWorldGridNodeHost { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(GridNodeHost.class); + protected static final IGridNodeListener NODE_LISTENER = new IGridNodeListener<>() { + @Override + public void onSaveChanges(MetaMachine nodeOwner, IGridNode node) { + nodeOwner.onChanged(); + } + + @Override + public void onStateChanged(MetaMachine nodeOwner, IGridNode node, IGridNodeListener.State state) { + if (nodeOwner instanceof IGridConnectedMachine machine) { + machine.onMainNodeStateChanged(state); + } + } + }; + @Getter private final IManagedGridNode mainNode; @@ -30,7 +41,7 @@ public class GridNodeHost extends MachineTrait implements IInWorldGridNodeHost { public GridNodeHost(MetaMachine machine) { super(machine); - this.mainNode = GridHelper.createManagedNode(machine, MachineNodeListener.INSTANCE) + this.mainNode = GridHelper.createManagedNode(machine, NODE_LISTENER) .setInWorldNode(true) .setVisualRepresentation(machine.getDefinition().getItem()); } @@ -85,4 +96,5 @@ public void loadCustomPersistedData(CompoundTag tag) { public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } + } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java deleted file mode 100644 index 57104e1a667..00000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/utils/MachineNodeListener.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gregtechceu.gtceu.integration.ae2.utils; - -import appeng.api.networking.IGridNode; -import appeng.api.networking.IGridNodeListener; -import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; - -public enum MachineNodeListener implements IGridNodeListener { - - INSTANCE; - - @Override - public void onSaveChanges(MetaMachine nodeOwner, IGridNode node) { - nodeOwner.onChanged(); - } - - @Override - public void onStateChanged(MetaMachine nodeOwner, IGridNode node, State state) { - if (nodeOwner instanceof IGridConnectedMachine machine) { - machine.onMainNodeStateChanged(state); - } - } -}