diff --git a/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java b/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java index 65f56a7369b..145ec034980 100644 --- a/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java +++ b/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java @@ -755,6 +755,22 @@ public enum MetaTileEntityIDs { SOLAR_PANEL_UV(2740), VENDING_MACHINE_CONTROLLER(2741), VENDING_MACHINE_UPLINK_ME(2742), + HATCH_OUTPUT_BUS_COMPRESSED_1(2743), + HATCH_OUTPUT_BUS_COMPRESSED_2(2744), + HATCH_OUTPUT_BUS_COMPRESSED_3(2745), + HATCH_OUTPUT_BUS_COMPRESSED_4(2746), + HATCH_OUTPUT_BUS_QUANTUM_1(2747), + HATCH_OUTPUT_BUS_QUANTUM_2(2748), + HATCH_OUTPUT_BUS_QUANTUM_3(2749), + HATCH_OUTPUT_BUS_QUANTUM_4(2750), + HATCH_INPUT_BUS_COMPRESSED_1(2751), + HATCH_INPUT_BUS_COMPRESSED_2(2752), + HATCH_INPUT_BUS_COMPRESSED_3(2753), + HATCH_INPUT_BUS_COMPRESSED_4(2754), + HATCH_INPUT_BUS_QUANTUM_1(2755), + HATCH_INPUT_BUS_QUANTUM_2(2756), + HATCH_INPUT_BUS_QUANTUM_3(2757), + HATCH_INPUT_BUS_QUANTUM_4(2758), INDUSTRIAL_LASER_ENGRAVER_CONTROLLER(3004), INDUSTRIAL_COMPRESSOR_CONTROLLER(3005), HIP_COMPRESSOR_CONTROLLER(3006), diff --git a/src/main/java/gregtech/api/enums/OutputBusType.java b/src/main/java/gregtech/api/enums/OutputBusType.java index ac392d10f5a..d73511ffd15 100644 --- a/src/main/java/gregtech/api/enums/OutputBusType.java +++ b/src/main/java/gregtech/api/enums/OutputBusType.java @@ -6,8 +6,10 @@ public enum OutputBusType { Void, + CompressedFiltered, StandardFiltered, MEFiltered, + CompressedUnfiltered, StandardUnfiltered, MEUnfiltered, // @@ -15,8 +17,8 @@ public enum OutputBusType { public boolean isFiltered() { return switch (this) { - case Void, StandardFiltered, MEFiltered -> true; - case StandardUnfiltered, MEUnfiltered -> false; + case Void, StandardFiltered, MEFiltered, CompressedFiltered -> true; + case StandardUnfiltered, MEUnfiltered, CompressedUnfiltered -> false; }; } } diff --git a/src/main/java/gregtech/api/gui/modularui/GTUITextures.java b/src/main/java/gregtech/api/gui/modularui/GTUITextures.java index b0dc84af75c..c5b18d70a18 100644 --- a/src/main/java/gregtech/api/gui/modularui/GTUITextures.java +++ b/src/main/java/gregtech/api/gui/modularui/GTUITextures.java @@ -568,4 +568,5 @@ public static FallbackableUITexture fallbackableProgressbar(String name, UITextu public static final UITexture PICTURE_ITEM_OUT = UITexture.fullImage(GregTech.ID, "gui/picture/item_out"); public static final UITexture PICTURE_FLUID_IN = UITexture.fullImage(GregTech.ID, "gui/picture/fluid_in"); public static final UITexture PICTURE_FLUID_OUT = UITexture.fullImage(GregTech.ID, "gui/picture/fluid_out"); + public static final UITexture SCREWDRIVER = UITexture.fullImage(GregTech.ID, "gui/overlay_button/screwdriver"); } diff --git a/src/main/java/gregtech/api/implementation/items/GTItemSink.java b/src/main/java/gregtech/api/implementation/items/GTItemSink.java new file mode 100644 index 00000000000..9004f0f5630 --- /dev/null +++ b/src/main/java/gregtech/api/implementation/items/GTItemSink.java @@ -0,0 +1,30 @@ +package gregtech.api.implementation.items; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.gtnhlib.capability.item.InventoryItemSink; + +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; + +public class GTItemSink extends InventoryItemSink { + + public GTItemSink(IMetaTileEntity imte, ForgeDirection side) { + super(imte, side); + } + + @Override + protected int getSlotStackLimit(int slot, ItemStack stack) { + // Cast here instead of storing it as a field because this is called in the super ctor, and by that point a + // field wouldn't have been set, leading to an NPE + int invStackLimit = ((IMetaTileEntity) inv).getStackSizeLimit(slot, stack); + + int existingMaxStack = stack == null ? 64 : stack.getMaxStackSize(); + + if (invStackLimit > 64) { + return invStackLimit / 64 * existingMaxStack; + } else { + return Math.min(invStackLimit, existingMaxStack); + } + } +} diff --git a/src/main/java/gregtech/api/interfaces/item/ItemStackSizeCalculator.java b/src/main/java/gregtech/api/interfaces/item/ItemStackSizeCalculator.java new file mode 100644 index 00000000000..a5d32b19504 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/item/ItemStackSizeCalculator.java @@ -0,0 +1,8 @@ +package gregtech.api.interfaces.item; + +import net.minecraft.item.ItemStack; + +public interface ItemStackSizeCalculator { + + int getSlotStackLimit(int slot, ItemStack stack); +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java index a77accc6ecd..1b11f730d8b 100644 --- a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java @@ -200,6 +200,16 @@ default void onUnload() {} */ boolean isValidSlot(int aIndex); + /** + * Gets the max stack size limit for a slot and a stack. + * + * @param slot The slot, or -1 for a general 'lowest slot' query. + * @param stack The stack, or null for a general 'any standard stack' query (getMaxStackSize() == 64). + */ + default int getStackSizeLimit(int slot, @Nullable ItemStack stack) { + return Math.min(getInventoryStackLimit(), stack == null ? 64 : stack.getMaxStackSize()); + } + /** * Check if the item at the specified index should be dropped * diff --git a/src/main/java/gregtech/api/metatileentity/CommonBaseMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/CommonBaseMetaTileEntity.java index 796e2de90ae..03749b3a05f 100644 --- a/src/main/java/gregtech/api/metatileentity/CommonBaseMetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/CommonBaseMetaTileEntity.java @@ -127,6 +127,9 @@ protected void saveMetaTileNBT(NBTTagCompound aNBT) { final NBTTagCompound tTag = new NBTTagCompound(); tTag.setInteger("IntSlot", i); tStack.writeToNBT(tTag); + if (tStack.stackSize > Byte.MAX_VALUE) { + tTag.setInteger("Count", tStack.stackSize); + } tItemList.appendTag(tTag); } } diff --git a/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java index 845e103e121..12befc647ef 100644 --- a/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java @@ -25,6 +25,10 @@ import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.UISettings; import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.gtnewhorizon.gtnhlib.capability.item.IItemIO; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSink; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSource; +import com.gtnewhorizon.gtnhlib.capability.item.InventoryItemSource; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -34,6 +38,7 @@ import gregtech.api.GregTechAPI; import gregtech.api.enums.GTValues; import gregtech.api.gui.modularui.GTUIInfos; +import gregtech.api.implementation.items.GTItemSink; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.modularui.IAddUIWidgets; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; @@ -109,6 +114,28 @@ protected CommonMetaTileEntity(String name, int invSlotCount) { @Nullable @Override public T getCapability(@NotNull Class capability, @NotNull ForgeDirection side) { + if (capability == IItemSink.class) { + return capability.cast(getItemSink(side)); + } + if (capability == IItemSource.class) { + return capability.cast(getItemSource(side)); + } + if (capability == IItemIO.class) { + return capability.cast(getItemIO(side)); + } + + return null; + } + + protected IItemSink getItemSink(ForgeDirection side) { + return new GTItemSink(this, side); + } + + protected IItemSource getItemSource(ForgeDirection side) { + return new InventoryItemSource(this, side); + } + + protected IItemIO getItemIO(ForgeDirection side) { return null; } diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEBasicMachine.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEBasicMachine.java index 876558f312c..18ef33091eb 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/MTEBasicMachine.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEBasicMachine.java @@ -16,7 +16,6 @@ import static gregtech.api.metatileentity.BaseTileEntity.UNUSED_SLOT_TOOLTIP; import static gregtech.api.util.GTRecipeConstants.EXPLODE; import static gregtech.api.util.GTRecipeConstants.ON_FIRE; -import static gregtech.api.util.GTUtility.moveMultipleItemStacks; import static net.minecraft.util.StatCollector.translateToLocal; import static net.minecraft.util.StatCollector.translateToLocalFormatted; import static net.minecraftforge.common.util.ForgeDirection.DOWN; @@ -84,6 +83,7 @@ import gregtech.api.recipe.metadata.CompressionTierKey; import gregtech.api.render.TextureFactory; import gregtech.api.util.GTClientPreference; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTLog; import gregtech.api.util.GTOreDictUnificator; import gregtech.api.util.GTRecipe; @@ -604,18 +604,14 @@ else if (mOutputFluid.isFluidEqual(getDrainableStack())) int tMaxStacks = (int) (tStoredEnergy / 64L); if (tMaxStacks > mOutputItems.length) tMaxStacks = mOutputItems.length; - moveMultipleItemStacks( - aBaseMetaTileEntity, - tTileEntity2, - aBaseMetaTileEntity.getFrontFacing(), - aBaseMetaTileEntity.getBackFacing(), - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1, - tMaxStacks); + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.outOfMachine(this, aBaseMetaTileEntity.getFrontFacing()); + transfer.dropItems(this); + + transfer.setStacksToTransfer(tMaxStacks); + + transfer.transfer(); } if (mOutputBlocked != 0) if (isOutputEmpty()) mOutputBlocked = 0; @@ -956,12 +952,17 @@ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aInde @Override public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, ItemStack aStack) { - if (side == mMainFacing || aIndex < getInputSlot() - || aIndex >= getInputSlot() + mInputSlotCount - || (!mAllowInputFromOutputSide && side == aBaseMetaTileEntity.getFrontFacing())) return false; - for (int i = getInputSlot(), j = i + mInputSlotCount; i < j; i++) - if (GTUtility.areStacksEqual(GTOreDictUnificator.get(aStack), mInventory[i]) && mDisableMultiStack) + if (side == mMainFacing) return false; + if (aIndex < getInputSlot()) return false; + if (aIndex >= getInputSlot() + mInputSlotCount) return false; + if (!mAllowInputFromOutputSide && side == aBaseMetaTileEntity.getFrontFacing()) return false; + + for (int i = getInputSlot(), j = i + mInputSlotCount; i < j; i++) { + if (GTUtility.areStacksEqual(GTOreDictUnificator.get(aStack), mInventory[i]) && mDisableMultiStack) { return i == aIndex; + } + } + return mDisableFilter || allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack); } diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEBuffer.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEBuffer.java index 9e253151baf..fb20484914d 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/MTEBuffer.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEBuffer.java @@ -13,10 +13,8 @@ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; +import java.util.OptionalInt; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.IntStream; @@ -28,6 +26,7 @@ import net.minecraft.util.StatCollector; import net.minecraftforge.common.util.ForgeDirection; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSink; import com.gtnewhorizons.modularui.api.drawable.UITexture; import com.gtnewhorizons.modularui.api.screen.ModularWindow; import com.gtnewhorizons.modularui.api.screen.UIBuildContext; @@ -40,6 +39,7 @@ import gregtech.api.interfaces.modularui.IAddUIWidgets; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTTooltipDataCache; import gregtech.api.util.GTUtility; @@ -334,7 +334,6 @@ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { || aTimer % 200 == 0 || mSuccess > 0)) { mSuccess--; - updateSlots(); moveItems(aBaseMetaTileEntity, aTimer); handleRedstoneOutput(aBaseMetaTileEntity); } @@ -344,35 +343,40 @@ protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { moveItems(aBaseMetaTileEntity, aTimer, 1); } - protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long ignoredTimer, int stacks) { - int tCost; - if (bStockingMode) tCost = GTUtility.moveMultipleItemStacks( - aBaseMetaTileEntity, - aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()), - aBaseMetaTileEntity.getBackFacing(), - aBaseMetaTileEntity.getFrontFacing(), - null, - false, - mTargetStackSize == 0 ? 64 : (byte) mTargetStackSize, - mTargetStackSize == 0 ? 1 : (byte) mTargetStackSize, - (byte) 64, - (byte) 1, - stacks); - else tCost = GTUtility.moveMultipleItemStacks( - aBaseMetaTileEntity, - aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()), - aBaseMetaTileEntity.getBackFacing(), - aBaseMetaTileEntity.getFrontFacing(), - null, - false, - (byte) 64, - (byte) 1, - mTargetStackSize == 0 ? 64 : (byte) mTargetStackSize, - mTargetStackSize == 0 ? 1 : (byte) mTargetStackSize, - stacks); - - if (tCost > 0 || aBaseMetaTileEntity.hasInventoryBeenModified()) { + protected void moveItems(IGregTechTileEntity igte, long ignoredTimer, int stacks) { + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.source(igte, ForgeDirection.UNKNOWN); + transfer.sink(igte.getTileEntityAtSide(igte.getBackFacing()), igte.getFrontFacing()); + + transfer.setStacksToTransfer(stacks); + + if (mTargetStackSize > 0) { + if (bStockingMode) { + IItemSink sink = transfer.getSink(); + + if (sink == null) return; + + OptionalInt stored = sink.getStoredAmount(null); + + if (!stored.isPresent()) return; + + int toTransfer = mTargetStackSize - stored.getAsInt(); + + transfer.setMaxTotalTransferred(toTransfer); + } else { + transfer.setFilter(stack -> stack.stackSize >= mTargetStackSize); + transfer.setMaxItemsPerTransfer(mTargetStackSize); + } + } + + if (transfer.transfer() > 0 || igte.hasInventoryBeenModified()) { mSuccess = 50; + + GTUtility.cleanInventory(this); + if (bSortStacks) { + GTUtility.compactInventory(this); + } } } @@ -393,42 +397,6 @@ public boolean allowGeneralRedstoneOutput() { return true; } - public void updateSlots() { - for (int i = 0; i < mInventory.length; i++) - if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; - if (bSortStacks) fillStacksIntoFirstSlots(); - } - - protected void fillStacksIntoFirstSlots() { - HashMap slots = new HashMap<>(mInventory.length); - HashMap stacks = new HashMap<>(mInventory.length); - List order = new ArrayList<>(mInventory.length); - List validSlots = new ArrayList<>(mInventory.length); - for (int i = 0; i < mInventory.length - 1; i++) { - if (!isValidSlot(i)) continue; - validSlots.add(i); - ItemStack s = mInventory[i]; - if (s == null) continue; - GTUtility.ItemId sID = GTUtility.ItemId.createNoCopy(s); - slots.merge(sID, s.stackSize, Integer::sum); - if (!stacks.containsKey(sID)) stacks.put(sID, s); - order.add(sID); - mInventory[i] = null; - } - int slotindex = 0; - for (GTUtility.ItemId sID : order) { - int toSet = slots.get(sID); - if (toSet == 0) continue; - int slot = validSlots.get(slotindex); - slotindex++; - mInventory[slot] = stacks.get(sID) - .copy(); - toSet = Math.min(toSet, mInventory[slot].getMaxStackSize()); - mInventory[slot].stackSize = toSet; - slots.merge(sID, toSet, (a, b) -> a - b); - } - } - @Override public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, float aX, float aY, float aZ, ItemStack aTool) { diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBus.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBus.java index eaa6db30c90..3397a773777 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBus.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBus.java @@ -303,11 +303,16 @@ protected void addOneStackLimitButton(ModularWindow.Builder builder) { }, GTUITextures.OVERLAY_BUTTON_ONE_STACK_LIMIT, () -> mTooltipCache.getData(ONE_STACK_LIMIT_TOOLTIP))); } - @Override - public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + protected void addInputBusUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { buildContext.addCloseListener(() -> uiButtonCount = 0); addSortStacksButton(builder); addOneStackLimitButton(builder); + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + addInputBusUIWidgets(builder, buildContext); + // Remove one for ghost circuit slot int slotCount = getSizeInventory(); if (allowSelectCircuit()) { diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBusCompressed.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBusCompressed.java new file mode 100644 index 00000000000..cfb6545eda1 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchInputBusCompressed.java @@ -0,0 +1,642 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; +import static net.minecraft.util.StatCollector.translateToLocal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.Nullable; + +import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSink; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSource; +import com.gtnewhorizon.gtnhlib.util.ItemUtil; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Color; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.SlotGroup; +import com.gtnewhorizons.modularui.common.widget.TextWidget; +import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget; + +import appeng.api.config.AccessRestriction; +import appeng.api.config.Actionable; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.MachineSource; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.IMEMonitorHandlerReceiver; +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.util.item.AEItemStack; +import gregtech.GTMod; +import gregtech.api.enums.ItemList; +import gregtech.api.gui.modularui.GTUITextures; +import gregtech.api.interfaces.IDataCopyable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.util.GTDataUtils; +import gregtech.api.util.GTUtility; +import gregtech.api.util.extensions.ArrayExt; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; +import gregtech.common.gui.modularui.util.ProxiedItemHandlerModifiable; +import gregtech.common.gui.modularui.widget.AEBaseSlot; +import gregtech.common.gui.modularui.widget.AESlotWidget; +import gregtech.common.inventory.AEInventory; +import gregtech.common.tileentities.machines.IRecipeProcessingAwareHatch; + +public class MTEHatchInputBusCompressed extends MTEHatchInputBus + implements IMEMonitor, IRecipeProcessingAwareHatch, IDataCopyable { + + public final int slotCount; + public final long stackCapacity; + public long stackLimitOverride; + + private final AEInventory inventory; + private final IItemHandlerModifiable itemHandler; + + private final int[] busSlots; + + private int processing = 0; + + private ItemStack[] originalStacks = null, containedStacks = null; + + public MTEHatchInputBusCompressed(int id, String name, String nameRegional, int tier, int slots, + long stackCapacity) { + super( + id, + name, + nameRegional, + tier, + 0, + ArrayExt.of( + "Item Input for Multiblocks", + "Shift + right click with screwdriver to turn Sort mode on/off", + "Capacity: " + slots + " slots, " + GTUtility.formatNumbers(stackCapacity) + " stacks each", + "Left click with data stick to save bus config", + "Right click with data stick to load bus config", + "Stores more than 1 stack per slot", + "Items cannot be extracted or inserted via the GUI", + "Can only be automated by GT and AE, low throughput solutions like Ender IO do not work!")); + + this.slotCount = slots; + this.stackCapacity = stackCapacity; + this.inventory = null; + this.busSlots = IntStream.range(0, slotCount) + .toArray(); + this.itemHandler = null; + } + + protected MTEHatchInputBusCompressed(MTEHatchInputBusCompressed prototype) { + super(prototype.mName, prototype.mTier, 1, prototype.mDescriptionArray, prototype.mTextures); + + this.slotCount = prototype.slotCount; + this.stackCapacity = prototype.stackCapacity; + this.stackLimitOverride = prototype.stackCapacity; + this.busSlots = prototype.busSlots; + + this.inventory = new BusInventory(slotCount); + + this.itemHandler = new ProxiedItemHandlerModifiable(inventory) { + + @Override + public int getSlots() { + return super.getSlots() + 1; + } + + @Override + public @Nullable ItemStack getStackInSlot(int slot) { + if (slot == getCircuitSlot()) return mInventory[0]; + + return super.getStackInSlot(slot); + } + + @Override + public void setStackInSlot(int slot, @Nullable ItemStack stack) { + if (slot == getCircuitSlot()) { + mInventory[0] = GTUtility.copyAmount(0, stack); + return; + } + + super.setStackInSlot(slot, stack); + } + }; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new MTEHatchInputBusCompressed(this); + } + + @Override + public boolean connectsToItemPipe(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public int getStackSizeLimit(int slot, @Nullable ItemStack stack) { + return GTUtility.longToInt(this.inventory.getAESlotLimit(slot, AEItemStack.create(stack))); + } + + @Override + public int getInventoryStackLimit() { + return getStackSizeLimit(-1, null); + } + + @Override + public int getCircuitSlot() { + return slotCount; + } + + @Override + public void startRecipeProcessing() { + if (processing == 0) { + originalStacks = GTDataUtils + .mapToArray(inventory.inventory, ItemStack[]::new, s -> s == null ? null : s.getItemStack()); + containedStacks = GTDataUtils.mapToArray(originalStacks, ItemStack[]::new, GTUtility::copy); + } + + processing++; + } + + @Override + public CheckRecipeResult endRecipeProcessing(MTEMultiBlockBase controller) { + processing--; + + if (processing == 0) { + for (int slotIndex = 0; slotIndex < slotCount; slotIndex++) { + ItemStack original = originalStacks[slotIndex]; + ItemStack contained = containedStacks[slotIndex]; + + if (original == null) continue; + + int delta = original.stackSize - (contained == null ? 0 : contained.stackSize); + + if (delta == 0) continue; + + if (delta < 0) { + GTMod.GT_FML_LOGGER.error( + "Compressed input bus somehow has more items in it than was original stored; this recipe will be cancelled (slot index={}, original={}, contained={}, delta={})", + slotIndex, + original, + contained, + delta); + controller.stopMachine(ShutDownReasonRegistry.CRITICAL_NONE); + return CheckRecipeResultRegistry.CRASH; + } + + IAEItemStack stack = inventory.getAEStackInSlot(slotIndex); + + if (stack == null || delta > stack.getStackSize()) { + GTMod.GT_FML_LOGGER.error( + "Compressed input bus somehow consumed more items than were available for this slot; this recipe will be cancelled (slot index={}, original={}, contained={}, delta={})", + slotIndex, + original, + contained, + delta); + controller.stopMachine(ShutDownReasonRegistry.CRITICAL_NONE); + return CheckRecipeResultRegistry.CRASH; + } + + stack.decStackSize(delta); + + inventory.setStackInSlot(slotIndex, stack.getStackSize() == 0 ? null : stack); + } + + originalStacks = null; + containedStacks = null; + } + + return CheckRecipeResultRegistry.SUCCESSFUL; + } + + @Override + public int getSizeInventory() { + return processing > 0 ? slotCount + 1 : 0; + } + + @Override + public ItemStack getStackInSlot(int slotIndex) { + if (processing > 0) { + if (slotIndex == getCircuitSlot()) return mInventory[0]; + + return GTDataUtils.getIndexSafe(containedStacks, slotIndex); + } else { + return null; + } + } + + @Override + public void setInventorySlotContents(int slotIndex, ItemStack toInsert) { + if (slotIndex == getCircuitSlot()) { + mInventory[0] = GTUtility.copyAmount(0, toInsert); + markDirty(); + return; + } + + if (GTUtility.isStackValid(toInsert)) { + inventory.insertItem(slotIndex, toInsert, false); + } + } + + @Override + public ItemStack decrStackSize(int index, int amount) { + return inventory.extractItem(index, amount, false); + } + + @Override + public boolean isItemValidForSlot(int slotIndex, ItemStack itemStack) { + return allowPutStack(getBaseMetaTileEntity(), slotIndex, ForgeDirection.UNKNOWN, itemStack); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity igte, int slotIndex, ForgeDirection side, ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity igte, int slotIndex, ForgeDirection side, ItemStack toInsert) { + if (slotIndex == getCircuitSlot()) return false; + if (side != ForgeDirection.UNKNOWN && side != getBaseMetaTileEntity().getFrontFacing()) return false; + if (mRecipeMap != null && !disableFilter && !mRecipeMap.containsInput(toInsert)) return false; + + IAEItemStack existing = inventory.getAEStackInSlot(slotIndex); + + if (existing != null) { + if (existing.getStackSize() >= inventory.getAESlotLimit(slotIndex, existing)) return false; + } + + if (!disableLimited) { + int containingSlot = inventory.indexOf(toInsert); + + if (containingSlot != -1) { + return containingSlot == slotIndex; + } + } + + return existing == null || existing.isSameType(toInsert); + } + + @Override + public IItemHandlerModifiable getInventoryHandler() { + return itemHandler; + } + + @Override + public IItemList getStorageList() { + return inventory.getStorageList(); + } + + @Override + public void addListener(IMEMonitorHandlerReceiver l, Object verificationToken) { + inventory.addListener(l, verificationToken); + } + + @Override + public void removeListener(IMEMonitorHandlerReceiver l) { + inventory.removeListener(l); + } + + @Override + public AccessRestriction getAccess() { + return inventory.getAccess(); + } + + @Override + public boolean isPrioritized(IAEItemStack input) { + return inventory.isPrioritized(input); + } + + @Override + public boolean canAccept(IAEItemStack input) { + return inventory.canAccept(input); + } + + @Override + public int getPriority() { + return inventory.getPriority(); + } + + @Override + public int getSlot() { + return inventory.getSlot(); + } + + @Override + public boolean validForPass(int i) { + return inventory.validForPass(i); + } + + @Override + public IAEItemStack injectItems(IAEItemStack input, Actionable type, BaseActionSource src) { + return inventory.injectItems(input, type, src); + } + + @Override + public IAEItemStack extractItems(IAEItemStack request, Actionable mode, BaseActionSource src) { + return inventory.extractItems(request, mode, src); + } + + @Override + public StorageChannel getChannel() { + return inventory.getChannel(); + } + + @Override + public void loadNBTData(NBTTagCompound tag) { + super.loadNBTData(tag); + + inventory.readFromNBT(tag.getCompoundTag("inv")); + stackLimitOverride = tag.getLong("stackLimitOverride"); + } + + @Override + public void saveNBTData(NBTTagCompound tag) { + super.saveNBTData(tag); + + tag.setTag("inv", inventory.writeToNBT(new NBTTagCompound())); + tag.setLong("stackLimitOverride", stackLimitOverride); + } + + protected void loadBusConfig(NBTTagCompound tag) { + stackLimitOverride = tag.getLong("stackLimitOverride"); + disableLimited = tag.getBoolean("disableLimited"); + disableFilter = tag.getBoolean("disableFilter"); + disableSort = tag.getBoolean("disableSort"); + + int circuit = tag.getInteger("circuit"); + + mInventory[0] = circuit == -1 ? null : GTUtility.getIntegratedCircuit(circuit); + } + + protected void saveBusConfig(NBTTagCompound tag) { + tag.setLong("stackLimitOverride", stackLimitOverride); + tag.setBoolean("disableLimited", disableLimited); + tag.setBoolean("disableFilter", disableFilter); + tag.setBoolean("disableSort", disableSort); + + tag.setInteger("circuit", mInventory[0] == null ? -1 : ItemUtil.getStackMeta(mInventory[0])); + } + + @Override + public boolean onRightclick(IGregTechTileEntity igte, EntityPlayer player) { + final ItemStack dataStick = player.inventory.getCurrentItem(); + + if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) { + openGui(player); + return true; + } + + if (!pasteCopiedData(player, dataStick.stackTagCompound)) { + if (player.worldObj.isRemote) { + player.addChatMessage(new ChatComponentTranslation("GT5U.machines.output_bus.invalid")); + } + + return false; + } else { + if (player.worldObj.isRemote) { + player.addChatMessage(new ChatComponentTranslation("GT5U.machines.output_bus.loaded")); + } + + return true; + } + } + + @Override + public void onLeftclick(IGregTechTileEntity igte, EntityPlayer player) { + final ItemStack held = player.inventory.getCurrentItem(); + + if (!ItemList.Tool_DataStick.isStackEqual(held, false, true)) { + return; + } + + held.stackTagCompound = getCopiedData(player); + held.setStackDisplayName("Output Bus Configuration"); + player.addChatMessage(new ChatComponentTranslation("GT5U.machines.output_bus.saved")); + } + + @Override + public NBTTagCompound getCopiedData(EntityPlayer player) { + final NBTTagCompound nbt = new NBTTagCompound(); + nbt.setString("type", mName); + saveBusConfig(nbt); + return nbt; + } + + @Override + public boolean pasteCopiedData(EntityPlayer player, NBTTagCompound nbt) { + if (nbt == null || !mName.equals(nbt.getString("type"))) return false; + loadBusConfig(nbt); + return true; + } + + @Override + public String getCopiedDataIdentifier(EntityPlayer player) { + return mName; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + addInputBusUIWidgets(builder, buildContext); + + List slots = new ArrayList<>(); + + SlotGroup slotGroup = SlotGroup.ofItemHandler(inventory, 4) + .startFromSlot(0) + .endAtSlot(slotCount - 1) + .slotCreator(index -> { + AEBaseSlot slot = new AEBaseSlot(inventory, index) { + + @Override + public void putStack(ItemStack stack) { + // no-op to disable MC's slot syncing, which truncates the >int max size, if present + } + }; + slots.add(slot); + return slot; + }) + .widgetCreator(slot -> new AESlotWidget(slot) { + + @Override + protected String getAmountTooltip() { + return GTUtility.translate( + "GT5U.gui.text.amount_out_of", + getAEStack().getStackSize(), + inventory.getAESlotLimit(slot.getSlotIndex())); + } + }) + .background(getGUITextureSet().getItemSlot()) + .canInsert(false) + .canTake(false) + .build(); + + builder.widget(slotGroup.setPos(52, 7)); + + builder.widget(new FakeSyncWidget<>(() -> { + slots.forEach(AEBaseSlot::onSlotChanged); + return inventory.writeToNBT(new NBTTagCompound()); + }, tag -> { + inventory.readFromNBT(tag); + slots.forEach(AEBaseSlot::onSlotChanged); + }, (buffer, tag) -> { + try { + buffer.writeNBTTagCompoundToBuffer(tag); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, buffer -> { + try { + return buffer.readNBTTagCompoundFromBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + })); + + builder.widget(createSettingsButton()); + buildContext.addSyncedWindow(SETTINGS_PANEL_WINDOW_ID, this::createSettingsPanel); + } + + private static final int SETTINGS_PANEL_WINDOW_ID = 8; + + private ButtonWidget createSettingsButton() { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (!widget.isClient()) { + widget.getContext() + .openSyncedWindow(SETTINGS_PANEL_WINDOW_ID); + } + }) + .setPlayClickSound(true) + .setBackground(() -> { + List ret = new ArrayList<>(); + ret.add(GTUITextures.BUTTON_STANDARD); + ret.add(GTUITextures.SCREWDRIVER); + return ret.toArray(new IDrawable[0]); + }) + .addTooltip(translateToLocal("GT5U.gui.button.compressed_bus_settings")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(new Pos2d(151, 6)) + .setSize(18, 18); + return (ButtonWidget) button; + } + + public ModularWindow createSettingsPanel(EntityPlayer player) { + final int w = 120; + final int h = 130; + final int parentW = getGUIWidth(); + final int parentH = getGUIHeight(); + + ModularWindow.Builder builder = ModularWindow.builder(w, h); + + builder.setBackground(GTUITextures.BACKGROUND_SINGLEBLOCK_DEFAULT); + builder.setGuiTint(getGUIColorization()); + builder.setDraggable(true); + builder.setPos( + (size, window) -> Alignment.Center.getAlignedPos(size, new Size(parentW, parentH)) + .add( + Alignment.TopRight.getAlignedPos(new Size(parentW, parentH), new Size(w, h)) + .add(w - 3, 0))); + + builder.widget( + new TextWidget(EnumChatFormatting.UNDERLINE + GTUtility.translate("GT5U.gui.text.bus_settings")) + .setPos(0, 2) + .setSize(120, 18)); + + builder.widget(new FakeSyncWidget.LongSyncer(() -> stackLimitOverride, val -> stackLimitOverride = val)); + + builder.widget( + TextWidget.localised("GT5U.gui.text.stack_capacity") + .setPos(0, 24) + .setSize(120, 18)); + + NumericWidget textField = (NumericWidget) new NumericWidget().setSetter(val -> stackLimitOverride = (long) val) + .setGetter(() -> stackLimitOverride) + .setValidator(val -> GTUtility.clamp((long) val, 1, stackCapacity)) + .setDefaultValue(stackCapacity) + .setScrollValues(1, 4, 64) + .setTextAlignment(Alignment.Center) + .setTextColor(Color.WHITE.normal) + .dynamicTooltip( + () -> Collections.singletonList(GTUtility.translate("GT5U.gui.text.rangedvalue", 1, stackCapacity))) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setSize(w - 12 * 2, 18) + .setPos(12, 40) + .setBackground(GTUITextures.BACKGROUND_TEXT_FIELD); + + builder.widget(textField); + + return builder.build(); + } + + @Override + protected IItemSource getItemSource(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing() ? inventory.getItemIO() : null; + } + + @Override + protected IItemSink getItemSink(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing() ? inventory.getItemIO() : null; + } + + class BusInventory extends AEInventory { + + public BusInventory(int slotCount) { + super(slotCount); + } + + @Override + public long getAESlotLimit(int slot, @Nullable IAEItemStack stack) { + int maxStack = stack == null ? 64 + : stack.getItemStack() + .getMaxStackSize(); + + return maxStack * stackLimitOverride; + } + + @Override + protected boolean allowPutStack(int slotIndex, IAEItemStack toInsert) { + IGregTechTileEntity igte = getBaseMetaTileEntity(); + + return MTEHatchInputBusCompressed.this.allowPutStack( + igte, + slotIndex, + ForgeDirection.UNKNOWN, + toInsert == null ? null : toInsert.getItemStack()); + } + + @Override + protected boolean allowPullStack(int slotIndex) { + return false; + } + + @Override + protected AEInventory copyImpl() { + return new MTEHatchInputBusCompressed.BusInventory(slotCount); + } + + @Override + protected BaseActionSource getActionSource() { + return new MachineSource((BaseMetaTileEntity) getBaseMetaTileEntity()); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBus.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBus.java index b0fbb7d5872..8f4aaf82ede 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBus.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBus.java @@ -5,13 +5,11 @@ import static gregtech.api.util.GTUtility.areStacksEqual; import static gregtech.api.util.GTUtility.isStackInvalid; import static gregtech.api.util.GTUtility.isStackValid; -import static gregtech.api.util.GTUtility.moveMultipleItemStacks; import java.util.BitSet; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ChatComponentTranslation; @@ -37,6 +35,7 @@ import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.render.TextureFactory; import gregtech.api.util.GTDataUtils; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; import gregtech.api.util.extensions.ArrayExt; @@ -235,29 +234,26 @@ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex return false; } + protected int getStackTransferAmount() { + return mInventory.length; + } + @Override public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { super.onPostTick(aBaseMetaTileEntity, aTick); if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && (aTick & 0x7) == 0 && pushOutputInventory()) { - final IInventory tTileEntity = aBaseMetaTileEntity - .getIInventoryAtSide(aBaseMetaTileEntity.getFrontFacing()); - if (tTileEntity != null) { - moveMultipleItemStacks( - aBaseMetaTileEntity, - tTileEntity, - aBaseMetaTileEntity.getFrontFacing(), - aBaseMetaTileEntity.getBackFacing(), - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1, - mInventory.length); - for (int i = 0; i < mInventory.length; i++) - if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; + + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.push(aBaseMetaTileEntity, aBaseMetaTileEntity.getFrontFacing()); + + transfer.setStacksToTransfer(getStackTransferAmount()); + transfer.setMaxItemsPerTransfer(getStackSizeLimit(-1, null)); + + if (transfer.transfer() > 0) { + GTUtility.cleanInventory(this); } } } diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBusCompressed.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBusCompressed.java new file mode 100644 index 00000000000..62ef55ad758 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEHatchOutputBusCompressed.java @@ -0,0 +1,480 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; +import static net.minecraft.util.StatCollector.translateToLocal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.Nullable; + +import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSink; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSource; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Color; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.SlotGroup; +import com.gtnewhorizons.modularui.common.widget.TextWidget; +import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget; + +import appeng.api.config.AccessRestriction; +import appeng.api.config.Actionable; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.MachineSource; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.IMEMonitorHandlerReceiver; +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.util.item.AEItemStack; +import gregtech.api.enums.OutputBusType; +import gregtech.api.gui.modularui.GTUITextures; +import gregtech.api.gui.widgets.PhantomItemButton; +import gregtech.api.interfaces.IOutputBusTransaction; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.util.GTUtility; +import gregtech.api.util.extensions.ArrayExt; +import gregtech.common.gui.modularui.widget.AEBaseSlot; +import gregtech.common.gui.modularui.widget.AESlotWidget; +import gregtech.common.inventory.AEInventory; + +public class MTEHatchOutputBusCompressed extends MTEHatchOutputBus implements IMEMonitor { + + public final int slotCount; + public final long stackCapacity; + public long stackLimitOverride; + + private final AEInventory inventory; + + private final int[] busSlots; + + public MTEHatchOutputBusCompressed(int id, String name, String nameRegional, int tier, int slots, + long stackCapacity) { + super( + id, + name, + nameRegional, + tier, + ArrayExt.of( + "Item Output for Multiblocks", + "Capacity: " + slots + " slots, " + GTUtility.formatNumbers(stackCapacity) + " stacks each", + "Left click with data stick to save bus config", + "Right click with data stick to load bus config", + "Stores more than 1 stack per slot", + "Items cannot be extracted or inserted via the GUI", + "Can only be automated by GT and AE, low throughput solutions like Ender IO do not work!"), + 0); + + this.slotCount = slots; + this.stackCapacity = stackCapacity; + this.inventory = null; + this.busSlots = IntStream.range(0, slotCount) + .toArray(); + } + + protected MTEHatchOutputBusCompressed(MTEHatchOutputBusCompressed prototype) { + super(prototype.mName, prototype.mTier, 0, prototype.mDescriptionArray, prototype.mTextures); + + this.slotCount = prototype.slotCount; + this.stackCapacity = prototype.stackCapacity; + this.stackLimitOverride = prototype.stackCapacity; + this.busSlots = prototype.busSlots; + + this.inventory = new BusInventory(slotCount); + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new MTEHatchOutputBusCompressed(this); + } + + @Override + public boolean connectsToItemPipe(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public int getStackSizeLimit(int slot, @Nullable ItemStack stack) { + return GTUtility.longToInt(this.inventory.getAESlotLimit(slot, AEItemStack.create(stack))); + } + + @Override + public int getInventoryStackLimit() { + return getStackSizeLimit(-1, null); + } + + @Override + protected int getStackTransferAmount() { + return slotCount; + } + + @Override + public OutputBusType getBusType() { + return lockedItem == null ? OutputBusType.CompressedUnfiltered : OutputBusType.CompressedFiltered; + } + + @Override + public boolean storePartial(ItemStack stack, boolean simulate) { + if (!simulate) markDirty(); + + IAEItemStack rejected = inventory.injectItems( + AEItemStack.create(stack), + simulate ? Actionable.SIMULATE : Actionable.MODULATE, + new MachineSource((BaseMetaTileEntity) getBaseMetaTileEntity())); + + stack.stackSize = (int) (rejected == null ? 0 : rejected.getStackSize()); + + return stack.stackSize == 0; + } + + @Override + public IItemHandlerModifiable getInventoryHandler() { + return inventory; + } + + @Override + public IItemList getStorageList() { + return inventory.getStorageList(); + } + + @Override + public void addListener(IMEMonitorHandlerReceiver l, Object verificationToken) { + inventory.addListener(l, verificationToken); + } + + @Override + public void removeListener(IMEMonitorHandlerReceiver l) { + inventory.removeListener(l); + } + + @Override + public AccessRestriction getAccess() { + return inventory.getAccess(); + } + + @Override + public boolean isPrioritized(IAEItemStack input) { + return inventory.isPrioritized(input); + } + + @Override + public boolean canAccept(IAEItemStack input) { + return inventory.canAccept(input); + } + + @Override + public int getPriority() { + return inventory.getPriority(); + } + + @Override + public int getSlot() { + return inventory.getSlot(); + } + + @Override + public boolean validForPass(int i) { + return inventory.validForPass(i); + } + + @Override + public IAEItemStack injectItems(IAEItemStack input, Actionable type, BaseActionSource src) { + return inventory.injectItems(input, type, src); + } + + @Override + public IAEItemStack extractItems(IAEItemStack request, Actionable mode, BaseActionSource src) { + return inventory.extractItems(request, mode, src); + } + + @Override + public StorageChannel getChannel() { + return inventory.getChannel(); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + + inventory.readFromNBT(aNBT.getCompoundTag("inv")); + loadBusConfig(aNBT); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + + aNBT.setTag("inv", inventory.writeToNBT(new NBTTagCompound())); + saveBusConfig(aNBT); + } + + protected void loadBusConfig(NBTTagCompound tag) { + stackLimitOverride = tag.getLong("stackLimitOverride"); + } + + protected void saveBusConfig(NBTTagCompound tag) { + tag.setLong("stackLimitOverride", stackLimitOverride); + } + + @Override + public NBTTagCompound getCopiedData(EntityPlayer player) { + final NBTTagCompound nbt = super.getCopiedData(player); + saveBusConfig(nbt); + return nbt; + } + + @Override + public boolean pasteCopiedData(EntityPlayer player, NBTTagCompound nbt) { + if (super.pasteCopiedData(player, nbt)) { + loadBusConfig(nbt); + return true; + } else { + return false; + } + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + List slots = new ArrayList<>(); + + SlotGroup slotGroup = SlotGroup.ofItemHandler(inventory, 4) + .startFromSlot(0) + .endAtSlot(slotCount - 1) + .slotCreator(index -> { + AEBaseSlot slot = new AEBaseSlot(inventory, index) { + + @Override + public void putStack(ItemStack stack) { + // no-op to disable MC's slot syncing, which truncates the >int max size, if present + } + }; + slots.add(slot); + return slot; + }) + .widgetCreator(slot -> new AESlotWidget(slot) { + + @Override + protected String getAmountTooltip() { + return GTUtility.translate( + "GT5U.gui.text.amount_out_of", + getAEStack().getStackSize(), + inventory.getAESlotLimit(slot.getSlotIndex())); + } + }) + .background(getGUITextureSet().getItemSlot()) + .canInsert(false) + .canTake(false) + .build(); + + builder.widget(slotGroup.setPos(52, 7)); + + builder.widget(new FakeSyncWidget<>(() -> { + slots.forEach(AEBaseSlot::onSlotChanged); + return inventory.writeToNBT(new NBTTagCompound()); + }, tag -> { + inventory.readFromNBT(tag); + slots.forEach(AEBaseSlot::onSlotChanged); + }, (buffer, tag) -> { + try { + buffer.writeNBTTagCompoundToBuffer(tag); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, buffer -> { + try { + return buffer.readNBTTagCompoundFromBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + })); + + if (acceptsItemLock()) { + builder.widget( + new PhantomItemButton(this).setPos(getGUIWidth() - 25, 40) + .setBackground(PhantomItemButton.FILTER_BACKGROUND)); + } + + builder.widget(createSettingsButton()); + buildContext.addSyncedWindow(SETTINGS_PANEL_WINDOW_ID, this::createSettingsPanel); + } + + private static final int SETTINGS_PANEL_WINDOW_ID = 8; + + private ButtonWidget createSettingsButton() { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (!widget.isClient()) { + widget.getContext() + .openSyncedWindow(SETTINGS_PANEL_WINDOW_ID); + } + }) + .setPlayClickSound(true) + .setBackground(() -> { + List ret = new ArrayList<>(); + ret.add(GTUITextures.BUTTON_STANDARD); + ret.add(GTUITextures.SCREWDRIVER); + return ret.toArray(new IDrawable[0]); + }) + .addTooltip(translateToLocal("GT5U.gui.button.compressed_bus_settings")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(new Pos2d(151, 6)) + .setSize(18, 18); + return (ButtonWidget) button; + } + + public ModularWindow createSettingsPanel(EntityPlayer player) { + final int w = 120; + final int h = 130; + final int parentW = getGUIWidth(); + final int parentH = getGUIHeight(); + + ModularWindow.Builder builder = ModularWindow.builder(w, h); + + builder.setBackground(GTUITextures.BACKGROUND_SINGLEBLOCK_DEFAULT); + builder.setGuiTint(getGUIColorization()); + builder.setDraggable(true); + builder.setPos( + (size, window) -> Alignment.Center.getAlignedPos(size, new Size(parentW, parentH)) + .add( + Alignment.TopRight.getAlignedPos(new Size(parentW, parentH), new Size(w, h)) + .add(w - 3, 0))); + + builder.widget( + new TextWidget(EnumChatFormatting.UNDERLINE + GTUtility.translate("GT5U.gui.text.bus_settings")) + .setPos(0, 2) + .setSize(120, 18)); + + builder.widget(new FakeSyncWidget.LongSyncer(() -> stackLimitOverride, val -> stackLimitOverride = val)); + + builder.widget( + TextWidget.localised("GT5U.gui.text.stack_capacity") + .setPos(0, 24) + .setSize(120, 18)); + + NumericWidget textField = (NumericWidget) new NumericWidget().setSetter(val -> stackLimitOverride = (long) val) + .setGetter(() -> stackLimitOverride) + .setValidator(val -> GTUtility.clamp((long) val, 1, stackCapacity)) + .setDefaultValue(stackCapacity) + .setScrollValues(1, 4, 64) + .setTextAlignment(Alignment.Center) + .setTextColor(Color.WHITE.normal) + .dynamicTooltip( + () -> Collections.singletonList(GTUtility.translate("GT5U.gui.text.rangedvalue", 1, stackCapacity))) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setSize(w - 12 * 2, 18) + .setPos(12, 40) + .setBackground(GTUITextures.BACKGROUND_TEXT_FIELD); + + builder.widget(textField); + + return builder.build(); + } + + @Override + public IOutputBusTransaction createTransaction() { + return new CompressedOutputBusTransaction(); + } + + @Override + protected IItemSource getItemSource(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing() ? inventory.getItemIO() : null; + } + + @Override + protected IItemSink getItemSink(ForgeDirection side) { + return null; + } + + class BusInventory extends AEInventory { + + public BusInventory(int slotCount) { + super(slotCount); + } + + @Override + public long getAESlotLimit(int slot, @Nullable IAEItemStack stack) { + int maxStack = stack == null ? 64 + : stack.getItemStack() + .getMaxStackSize(); + + return maxStack * stackLimitOverride; + } + + @Override + protected AEInventory copyImpl() { + return new BusInventory(slotCount); + } + + @Override + protected BaseActionSource getActionSource() { + return new MachineSource((BaseMetaTileEntity) getBaseMetaTileEntity()); + } + } + + class CompressedOutputBusTransaction implements IOutputBusTransaction { + + private final AEInventory inventory; + + private boolean active = true; + + CompressedOutputBusTransaction() { + inventory = getBus().inventory.copy(); + } + + @Override + public MTEHatchOutputBusCompressed getBus() { + return MTEHatchOutputBusCompressed.this; + } + + @Override + public boolean storePartial(GTUtility.ItemId id, ItemStack stack) { + if (!active) throw new IllegalStateException("Cannot add to a transaction after committing it"); + + IAEItemStack rejected = inventory.injectItems(AEItemStack.create(stack), Actionable.MODULATE, null); + + stack.stackSize = rejected == null ? 0 : (int) rejected.getStackSize(); + + return false; + } + + @Override + public void completeItem(GTUtility.ItemId id) { + // Do nothing + } + + @Override + public boolean hasAvailableSpace() { + return true; + } + + @Override + public void commit() { + for (int i = 0; i < slotCount; i++) { + getBus().inventory.setStackInSlot(i, this.inventory.getAEStackInSlot(i)); + } + + getBus().markDirty(); + + active = false; + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEItemPipe.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEItemPipe.java index 79a51fffd57..de91ba08760 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/MTEItemPipe.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEItemPipe.java @@ -312,18 +312,8 @@ public boolean insertItemStackIntoTileEntity(Object aSender, ForgeDirection side if ((!(tInventory instanceof TileEntityHopper) && !(tInventory instanceof TileEntityDispenser)) || getBaseMetaTileEntity().getMetaIDAtSide(side) != side.getOpposite() .ordinal()) { - return GTUtility.moveMultipleItemStacks( - aSender, - tInventory, - ForgeDirection.UNKNOWN, - side.getOpposite(), - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1, - 1) > 0; + // TODO: to be properly fixed in the item pipe PR + return true; } } } diff --git a/src/main/java/gregtech/api/objects/GTItemStack.java b/src/main/java/gregtech/api/objects/GTItemStack.java index 2664c581abd..c3ac7edca69 100644 --- a/src/main/java/gregtech/api/objects/GTItemStack.java +++ b/src/main/java/gregtech/api/objects/GTItemStack.java @@ -44,9 +44,11 @@ public boolean equals(ItemStack a, ItemStack b) { public int hashCode(ItemStack o) { int hash = Fnv1a32.initialState(); - hash = Fnv1a32.hashStep(hash, Objects.hashCode(o.getItem())); - hash = Fnv1a32.hashStep(hash, Items.feather.getDamage(o)); - hash = Fnv1a32.hashStep(hash, Objects.hashCode(o.getTagCompound())); + if (o != null) { + hash = Fnv1a32.hashStep(hash, Objects.hashCode(o.getItem())); + hash = Fnv1a32.hashStep(hash, Items.feather.getDamage(o)); + hash = Fnv1a32.hashStep(hash, Objects.hashCode(o.getTagCompound())); + } return hash; } diff --git a/src/main/java/gregtech/api/util/GTDataUtils.java b/src/main/java/gregtech/api/util/GTDataUtils.java index 0abb1dce5cd..40d3d86d6b3 100644 --- a/src/main/java/gregtech/api/util/GTDataUtils.java +++ b/src/main/java/gregtech/api/util/GTDataUtils.java @@ -11,6 +11,8 @@ import java.util.function.Predicate; import java.util.stream.Stream; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectIterators; /** @@ -173,4 +175,19 @@ public static T[] concat(T[]... arrays) { return out; } + + public static int[] intersect(int[] a, int[] b) { + IntLinkedOpenHashSet a2 = new IntLinkedOpenHashSet(a); + IntLinkedOpenHashSet b2 = new IntLinkedOpenHashSet(b); + + IntArrayList out = new IntArrayList(); + + a2.forEach((int i) -> { + if (b2.contains(i)) { + out.add(i); + } + }); + + return out.toIntArray(); + } } diff --git a/src/main/java/gregtech/api/util/GTItemTransfer.java b/src/main/java/gregtech/api/util/GTItemTransfer.java new file mode 100644 index 00000000000..29647f455e0 --- /dev/null +++ b/src/main/java/gregtech/api/util/GTItemTransfer.java @@ -0,0 +1,50 @@ +package gregtech.api.util; + +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; +import com.gtnewhorizon.gtnhlib.capability.item.ItemTransfer; + +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords; +import gregtech.api.metatileentity.implementations.MTEBasicMachine; + +public class GTItemTransfer extends ItemTransfer { + + public void push(IHasWorldObjectAndCoords self, ForgeDirection side) { + push(self, side, self.getTileEntityAtSide(side)); + } + + public void pull(IHasWorldObjectAndCoords self, ForgeDirection side) { + pull(self, side, self.getTileEntityAtSide(side)); + } + + public void outOfMachine(IGregTechTileEntity igte, ForgeDirection side) { + outOfMachine(igte.getMetaTileEntity(), side); + } + + public void outOfMachine(IMetaTileEntity imte, ForgeDirection side) { + source(imte, ForgeDirection.UNKNOWN); + sink( + imte.getBaseMetaTileEntity() + .getTileEntityAtSide(side), + side.getOpposite()); + } + + public void dropItems(IHasWorldObjectAndCoords tile, ForgeDirection output) { + dropItems( + tile.getWorld(), + new BlockPos( + tile.getXCoord() + output.offsetX, + tile.getYCoord() + output.offsetY, + tile.getZCoord() + output.offsetZ)); + } + + public void dropItems(MTEBasicMachine machine) { + dropItems( + machine.getBaseMetaTileEntity(), + machine.getBaseMetaTileEntity() + .getFrontFacing()); + } +} diff --git a/src/main/java/gregtech/api/util/GTUtility.java b/src/main/java/gregtech/api/util/GTUtility.java index ee406480f7a..ebda0bca078 100644 --- a/src/main/java/gregtech/api/util/GTUtility.java +++ b/src/main/java/gregtech/api/util/GTUtility.java @@ -28,6 +28,7 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.AbstractCollection; +import java.util.AbstractList; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -73,7 +74,6 @@ import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; -import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; @@ -88,7 +88,6 @@ import net.minecraft.potion.PotionEffect; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; -import net.minecraft.tileentity.TileEntityChest; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChatComponentText; import net.minecraft.util.DamageSource; @@ -139,6 +138,7 @@ import cofh.api.transport.IItemDuct; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.registry.GameRegistry; +import fox.spiteful.avaritia.items.ItemMatterCluster; import gregtech.GTMod; import gregtech.api.GregTechAPI; import gregtech.api.damagesources.GTDamageSources; @@ -160,6 +160,7 @@ import gregtech.api.interfaces.IHasIndexedTexture; import gregtech.api.interfaces.IProjectileItem; import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.item.ItemStackSizeCalculator; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IBasicEnergyContainer; import gregtech.api.interfaces.tileentity.ICoverable; @@ -187,6 +188,9 @@ import ic2.core.IC2Potion; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap; /** @@ -647,1104 +651,190 @@ public static boolean isConnectableNonInventoryPipe(TileEntity tileEntity, Forge return Translocator.isModLoaded() && tileEntity instanceof codechicken.translocator.TileItemTranslocator; } - /** - * Moves Stack from Inv-Slot to Inv-Slot, without checking if its even allowed. - * - * @return the Amount of moved Items - */ - public static byte moveStackIntoPipe(IInventory aTileEntity1, Object aTileEntity2, int[] aGrabSlots, - ForgeDirection fromSide, ForgeDirection putSide, List aFilter, boolean aInvertFilter, - byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce) { - return moveStackIntoPipe( - aTileEntity1, - aTileEntity2, - aGrabSlots, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - true); - } + public static List wrapInventory(IInventory inv) { + int sizeInventory = inv.getSizeInventory(); - /** - * Moves Stack from Inv-Slot to Inv-Slot, without checking if it is even allowed. - * - * @return the Amount of moved Items - */ - public static byte moveStackIntoPipe(IInventory fromInventory, Object toObject, int[] fromSlots, - ForgeDirection fromSide, ForgeDirection putSide, List aFilter, boolean aInvertFilter, - byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, - boolean dropItem) { - if (fromInventory == null || aMinTargetStackSize <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMaxMoveAtOnce <= 0 - || aMinMoveAtOnce > aMaxMoveAtOnce) return 0; - if (toObject != null) { - checkAvailabilities(); - if (TE_CHECK && toObject instanceof IItemDuct itemDuct) { - for (final int aGrabSlot : fromSlots) { - if (listContainsItem(aFilter, fromInventory.getStackInSlot(aGrabSlot), true, aInvertFilter)) { - if (isAllowedToTakeFromSlot( - fromInventory, - aGrabSlot, - fromSide, - fromInventory.getStackInSlot(aGrabSlot))) { - if (Math.max(aMinMoveAtOnce, aMinTargetStackSize) - <= fromInventory.getStackInSlot(aGrabSlot).stackSize) { - ItemStack tStack = copyAmount( - Math.min( - fromInventory.getStackInSlot(aGrabSlot).stackSize, - Math.min(aMaxMoveAtOnce, aMaxTargetStackSize)), - fromInventory.getStackInSlot(aGrabSlot)); - ItemStack rStack = itemDuct.insertItem(putSide, copyOrNull(tStack)); - byte tMovedItemCount = (byte) (tStack.stackSize - - (rStack == null ? 0 : rStack.stackSize)); - if (tMovedItemCount >= 1 /* Math.max(aMinMoveAtOnce, aMinTargetStackSize) */) { - fromInventory.decrStackSize(aGrabSlot, tMovedItemCount); - fromInventory.markDirty(); - return tMovedItemCount; - } - } - } - } - } - return 0; - } - if (BC_CHECK && toObject instanceof buildcraft.api.transport.IPipeTile bcPipe) { - for (int fromSlot : fromSlots) { - if (listContainsItem(aFilter, fromInventory.getStackInSlot(fromSlot), true, aInvertFilter)) { - if (isAllowedToTakeFromSlot( - fromInventory, - fromSlot, - fromSide, - fromInventory.getStackInSlot(fromSlot))) { - if (Math.max(aMinMoveAtOnce, aMinTargetStackSize) - <= fromInventory.getStackInSlot(fromSlot).stackSize) { - ItemStack tStack = copyAmount( - Math.min( - fromInventory.getStackInSlot(fromSlot).stackSize, - Math.min(aMaxMoveAtOnce, aMaxTargetStackSize)), - fromInventory.getStackInSlot(fromSlot)); - byte tMovedItemCount = (byte) bcPipe.injectItem(copyOrNull(tStack), false, putSide); - if (tMovedItemCount >= Math.max(aMinMoveAtOnce, aMinTargetStackSize)) { - tMovedItemCount = (byte) (bcPipe - .injectItem(copyAmount(tMovedItemCount, tStack), true, putSide)); - fromInventory.decrStackSize(fromSlot, tMovedItemCount); - fromInventory.markDirty(); - return tMovedItemCount; - } - } - } - } - } - return 0; + return new AbstractList<>() { + + @Override + public ItemStack get(int index) { + return inv.getStackInSlot(index); } - } - if (fromInventory instanceof TileEntity fromTileEntity && fromSide != ForgeDirection.UNKNOWN - && fromSide.getOpposite() == ForgeDirection.getOrientation(putSide.ordinal())) { - int tX = fromTileEntity.xCoord + fromSide.offsetX, tY = fromTileEntity.yCoord + fromSide.offsetY, - tZ = fromTileEntity.zCoord + fromSide.offsetZ; - if (!hasBlockHitBox(((TileEntity) fromInventory).getWorldObj(), tX, tY, tZ) && dropItem) { - for (final int fromSlot : fromSlots) { - if (listContainsItem(aFilter, fromInventory.getStackInSlot(fromSlot), true, aInvertFilter)) { - if (isAllowedToTakeFromSlot( - fromInventory, - fromSlot, - fromSide, - fromInventory.getStackInSlot(fromSlot))) { - if (Math.max(aMinMoveAtOnce, aMinTargetStackSize) - <= fromInventory.getStackInSlot(fromSlot).stackSize) { - final ItemStack tStack = copyAmount( - Math.min( - fromInventory.getStackInSlot(fromSlot).stackSize, - Math.min(aMaxMoveAtOnce, aMaxTargetStackSize)), - fromInventory.getStackInSlot(fromSlot)); - final EntityItem tEntity = new EntityItem( - ((TileEntity) fromInventory).getWorldObj(), - tX + 0.5, - tY + 0.5, - tZ + 0.5, - tStack); - tEntity.motionX = tEntity.motionY = tEntity.motionZ = 0; - ((TileEntity) fromInventory).getWorldObj() - .spawnEntityInWorld(tEntity); - assert tStack != null; - fromInventory.decrStackSize(fromSlot, tStack.stackSize); - fromInventory.markDirty(); - return (byte) tStack.stackSize; - } - } - } - } + @Override + public ItemStack set(int index, ItemStack element) { + ItemStack existing = inv.getStackInSlot(index); + inv.setInventorySlotContents(index, element); + return existing; } - } - return 0; - } - /** - * Moves Stack from Inv-Slot to Inv-Slot, without checking if its even allowed. (useful for internal Inventory - * Operations) - * - * @return the Amount of moved Items - */ - public static byte moveStackFromSlotAToSlotB(IInventory aTileEntity1, IInventory aTileEntity2, int aGrabFrom, - int aPutTo, byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce) { - if (aTileEntity1 == null || aTileEntity2 == null - || aMinTargetStackSize <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMaxMoveAtOnce <= 0 - || aMinMoveAtOnce > aMaxMoveAtOnce) return 0; - - ItemStack tStack1 = aTileEntity1.getStackInSlot(aGrabFrom), tStack2 = aTileEntity2.getStackInSlot(aPutTo), - tStack3; - if (tStack1 != null) { - if (tStack2 != null && !areStacksEqual(tStack1, tStack2)) return 0; - tStack3 = copyOrNull(tStack1); - aMaxTargetStackSize = (byte) Math.min( - aMaxTargetStackSize, - Math.min( - tStack3.getMaxStackSize(), - Math.min( - tStack2 == null ? Integer.MAX_VALUE : tStack2.getMaxStackSize(), - aTileEntity2.getInventoryStackLimit()))); - tStack3.stackSize = Math - .min(tStack3.stackSize, aMaxTargetStackSize - (tStack2 == null ? 0 : tStack2.stackSize)); - if (tStack3.stackSize > aMaxMoveAtOnce) tStack3.stackSize = aMaxMoveAtOnce; - if (tStack3.stackSize + (tStack2 == null ? 0 : tStack2.stackSize) - >= Math.min(tStack3.getMaxStackSize(), aMinTargetStackSize) && tStack3.stackSize >= aMinMoveAtOnce) { - tStack3 = aTileEntity1.decrStackSize(aGrabFrom, tStack3.stackSize); - aTileEntity1.markDirty(); - if (tStack3 != null) { - if (tStack2 == null) { - aTileEntity2.setInventorySlotContents(aPutTo, copyOrNull(tStack3)); - } else { - tStack2.stackSize += tStack3.stackSize; - } - aTileEntity2.markDirty(); - return (byte) tStack3.stackSize; - } + @Override + public int size() { + return sizeInventory; } - } - return 0; + }; } - public static boolean isAllowedToTakeFromSlot(IInventory aTileEntity, int aSlot, ForgeDirection side, - ItemStack aStack) { - if (side == ForgeDirection.UNKNOWN) { - return Arrays.stream(ForgeDirection.VALID_DIRECTIONS) - .anyMatch(d -> isAllowedToTakeFromSlot(aTileEntity, aSlot, d, aStack)); - } - if (aTileEntity instanceof ISidedInventory sided) return sided.canExtractItem(aSlot, aStack, side.ordinal()); - return true; - } + public static void compactStandardInventory(IInventory inv) { + inv.markDirty(); - public static boolean isAllowedToPutIntoSlot(IInventory aTileEntity, int aSlot, ForgeDirection side, - ItemStack aStack, byte aMaxStackSize) { - ItemStack tStack = aTileEntity.getStackInSlot(aSlot); - if (tStack != null && (!areStacksEqual(tStack, aStack) || tStack.stackSize >= tStack.getMaxStackSize())) - return false; - if (side == ForgeDirection.UNKNOWN) { - return Arrays.stream(ForgeDirection.VALID_DIRECTIONS) - .anyMatch(d -> isAllowedToPutIntoSlot(aTileEntity, aSlot, d, aStack, aMaxStackSize)); - } - if (aTileEntity instanceof ISidedInventory - && !((ISidedInventory) aTileEntity).canInsertItem(aSlot, aStack, side.ordinal())) return false; - return aSlot < aTileEntity.getSizeInventory() && aTileEntity.isItemValidForSlot(aSlot, aStack); + ItemStackSizeCalculator stackSizes = (slot, stack) -> Math + .min(inv.getInventoryStackLimit(), stack == null ? 64 : stack.getMaxStackSize()); + + compactInventory(wrapInventory(inv), stackSizes); } - /** - * moves multiple stacks from Inv-Side to Inv-Side - * - * @return the Amount of moved Items - */ - public static int moveMultipleItemStacks(Object aTileEntity1, Object aTileEntity2, ForgeDirection fromSide, - ForgeDirection putSide, List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, - byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, int aStackAmount) { - if (aTileEntity1 instanceof IInventory) return moveMultipleItemStacks( - (IInventory) aTileEntity1, - aTileEntity2, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aStackAmount, - true); - return 0; + public static void compactInventory(IMetaTileEntity imte) { + compactInventory(imte, 0, imte.getSizeInventory()); } - public static int moveMultipleItemStacks(IInventory fromInventory, Object toObject, ForgeDirection fromSide, - ForgeDirection putSide, List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, - byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, int aMaxStackTransfer, - boolean aDoCheckChests) { - if (fromInventory == null || aMinTargetStackSize <= 0 - || aMaxMoveAtOnce <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMinMoveAtOnce > aMaxMoveAtOnce - || aMaxStackTransfer == 0) return 0; - - // find where to take from - final int[] tGrabSlots = new int[fromInventory.getSizeInventory()]; - int tGrabSlotsSize = 0; - if (fromInventory instanceof ISidedInventory) { - for (int i : ((ISidedInventory) fromInventory).getAccessibleSlotsFromSide(fromSide.ordinal())) { - final ItemStack s = fromInventory.getStackInSlot(i); - if (s == null || !isAllowedToTakeFromSlot(fromInventory, i, fromSide, s) - || s.stackSize < aMinMoveAtOnce - || !listContainsItem(aFilter, s, true, aInvertFilter)) continue; - tGrabSlots[tGrabSlotsSize++] = i; - } - } else { - for (int i = 0; i < tGrabSlots.length; i++) { - ItemStack s = fromInventory.getStackInSlot(i); - if (s == null || s.stackSize < aMinMoveAtOnce || !listContainsItem(aFilter, s, true, aInvertFilter)) - continue; - tGrabSlots[tGrabSlotsSize++] = i; - } - } + public static void compactInventory(IMetaTileEntity imte, int start, int end) { + imte.markDirty(); - // no source, bail out - if (tGrabSlotsSize == 0) { - // maybe source is a double chest. check it - if (aDoCheckChests && fromInventory instanceof TileEntityChest chest) return moveFromAdjacentChests( - chest, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer); - return 0; - } - - // if target is an inventory, e.g. chest, machine, drawers... - if (toObject instanceof IInventory toInventory) { - - // partially filled slot spare space mapping. - // value is the sum of all spare space left not counting completely empty slot - final HashMap tPutItems = new HashMap<>(toInventory.getSizeInventory()); - // partially filled slot contents - final HashMap> tPutItemStacks = new HashMap<>(toInventory.getSizeInventory()); - // completely empty slots - final List tPutFreeSlots = new ArrayList<>(toInventory.getSizeInventory()); - - // find possible target slots - int[] accessibleSlots = null; - if (toObject instanceof ISidedInventory sided) - accessibleSlots = sided.getAccessibleSlotsFromSide(putSide.ordinal()); - for (int i = 0; i < toInventory.getSizeInventory(); i++) { - int slot = i; - if (accessibleSlots != null) { - if (accessibleSlots.length <= i) break; - slot = accessibleSlots[slot]; - } - ItemStack s = toInventory.getStackInSlot(slot); - if (s == null) { - tPutFreeSlots.add(slot); - } else if ((s.stackSize < s.getMaxStackSize() && s.stackSize < toInventory.getInventoryStackLimit()) - && aMinMoveAtOnce <= s.getMaxStackSize() - s.stackSize - && isAllowedToPutIntoSlot(toInventory, slot, putSide, s, (byte) 64)) { - ItemId sID = ItemId.createNoCopy(s); - tPutItems.merge( - sID, - (Math.min(s.getMaxStackSize(), toInventory.getInventoryStackLimit()) - s.stackSize), - Integer::sum); - tPutItemStacks.computeIfAbsent(sID, k -> new ArrayList<>()) - .add(s); - } - } + ItemStackSizeCalculator stackSizes = (slot, stack) -> imte.getStackSizeLimit(slot + start, stack); - // target completely filled, bail out - if (tPutItems.isEmpty() && tPutFreeSlots.isEmpty()) { - // maybe target is a double chest. check it. - if (aDoCheckChests && toObject instanceof TileEntityChest chest) return moveToAdjacentChests( - fromInventory, - chest, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer); - return 0; - } + compactInventory(wrapInventory(imte).subList(start, end), stackSizes); + } - // go over source stacks one by one - int tStacksMoved = 0, tTotalItemsMoved = 0; - for (int j = 0; j < tGrabSlotsSize; j++) { - final int grabSlot = tGrabSlots[j]; - int tMovedItems; - int tStackSize; - do { - tMovedItems = 0; - final ItemStack tGrabStack = fromInventory.getStackInSlot(grabSlot); - if (tGrabStack == null) break; - tStackSize = tGrabStack.stackSize; - final ItemId sID = ItemId.createNoCopy(tGrabStack); - - if (tPutItems.containsKey(sID)) { - // there is a partially filled slot, try merging - final int canPut = Math.min(tPutItems.get(sID), aMaxMoveAtOnce); - if (canPut >= aMinMoveAtOnce) { - final List putStack = tPutItemStacks.get(sID); - if (!putStack.isEmpty()) { - // can move, do merge - int toPut = Math.min(canPut, tStackSize); - tMovedItems = toPut; - for (int i = 0; i < putStack.size(); i++) { - final ItemStack s = putStack.get(i); - final int sToPut = Math.min( - Math.min( - Math.min(toPut, s.getMaxStackSize() - s.stackSize), - toInventory.getInventoryStackLimit() - s.stackSize), - aMaxTargetStackSize - s.stackSize); - if (sToPut <= 0) continue; - if (sToPut < aMinMoveAtOnce) continue; - if (s.stackSize + sToPut < aMinTargetStackSize) continue; - toPut -= sToPut; - s.stackSize += sToPut; - if (s.stackSize == s.getMaxStackSize() - || s.stackSize == toInventory.getInventoryStackLimit()) { - // this slot is full. remove this stack from candidate list - putStack.remove(i); - i--; - } - if (toPut == 0) break; - } - tMovedItems -= toPut; - if (tMovedItems > 0) { - tStackSize -= tMovedItems; - tTotalItemsMoved += tMovedItems; - // deduct spare space - tPutItems.merge(sID, tMovedItems, (a, b) -> a.equals(b) ? null : a - b); - - if (tStackSize == 0) fromInventory.setInventorySlotContents(grabSlot, null); - else tGrabStack.stackSize = tStackSize; - - fromInventory.markDirty(); - toInventory.markDirty(); - } - } - } - } - // still stuff to move & have completely empty slots - if (tStackSize > 0 && !tPutFreeSlots.isEmpty()) { - for (int i = 0; i < tPutFreeSlots.size(); i++) { - final int tPutSlot = tPutFreeSlots.get(i); - if (isAllowedToPutIntoSlot(toInventory, tPutSlot, putSide, tGrabStack, (byte) 64)) { - // allowed, now do moving - final int tMoved = moveStackFromSlotAToSlotB( - fromInventory, - toInventory, - grabSlot, - tPutSlot, - aMaxTargetStackSize, - aMinTargetStackSize, - (byte) (aMaxMoveAtOnce - tMovedItems), - aMinMoveAtOnce); - if (tMoved > 0) { - final ItemStack s = toInventory.getStackInSlot(tPutSlot); - if (s != null) { - // s might be null if tPutInventory is very special, e.g. infinity chest - // if s is null, we will not mark this slot as target candidate for anything - final int spare = Math - .min(s.getMaxStackSize(), toInventory.getInventoryStackLimit()) - - s.stackSize; - if (spare > 0) { - final ItemId ssID = ItemId.createNoCopy(s); - // add back to spare space count - tPutItems.merge(ssID, spare, Integer::sum); - // add to partially filled slot list - tPutItemStacks.computeIfAbsent(ssID, k -> new ArrayList<>()) - .add(s); - } - // this is no longer free - tPutFreeSlots.remove(i); - i--; - } - // else -> noop - // this is still a free slot. no need to do anything. - tTotalItemsMoved += tMoved; - tMovedItems += tMoved; - tStackSize -= tMoved; - if (tStackSize == 0) break; - } - } - } - } + public static void compactInventory(List inv, ItemStackSizeCalculator stackSizes) { + int len = inv.size(); - if (tMovedItems > 0) { - // check if we have moved enough stacks - if (++tStacksMoved >= aMaxStackTransfer) return tTotalItemsMoved; - } - } while (tMovedItems > 0 && tStackSize > 0); // support inventories that store more than a stack in a - // slot - } + // Filter each ItemStack into their own lists (grouped by Item, meta, and NBT). + Map>> slots = new Object2ObjectOpenCustomHashMap<>( + GTItemStack.ITEMSTACK_HASH_STRATEGY_NBT_SENSITIVE); - // check if source is a double chest, if yes, try move from the adjacent as well - if (aDoCheckChests && fromInventory instanceof TileEntityChest chest) { - final int tAmount = moveFromAdjacentChests( - chest, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer - tStacksMoved); - if (tAmount != 0) return tAmount + tTotalItemsMoved; - } + for (int i = 0; i < len; i++) { + ItemStack stack = inv.get(i); - // check if target is a double chest, if yes, try move to the adjacent as well - if (aDoCheckChests && toObject instanceof TileEntityChest chest) { - final int tAmount = moveToAdjacentChests( - fromInventory, - chest, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer - tStacksMoved); - if (tAmount != 0) return tAmount + tTotalItemsMoved; - } + if (stack == null) continue; - return tTotalItemsMoved; - } - // there should be a function to transfer more than 1 stack in a pipe - // however I do not see any ways to improve it. too much work for what it is worth - int tTotalItemsMoved = 0; - final int tGrabInventorySize = tGrabSlots.length; - for (int i = 0; i < tGrabInventorySize; i++) { - final int tMoved = moveStackIntoPipe( - fromInventory, - toObject, - tGrabSlots, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aDoCheckChests); - if (tMoved == 0) return tTotalItemsMoved; - else tTotalItemsMoved += tMoved; + slots.computeIfAbsent(stack, ignored -> new ObjectArrayList<>()) + .add(ObjectIntPair.of(stack, i)); } - return 0; - } - private static int moveToAdjacentChests(IInventory aTileEntity1, TileEntityChest aTargetChest, - ForgeDirection fromSide, ForgeDirection putSide, List aFilter, boolean aInvertFilter, - byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, - int aMaxStackTransfer) { - if (aTargetChest.adjacentChestChecked) { - if (aTargetChest.adjacentChestXNeg != null) { - return moveMultipleItemStacks( - aTileEntity1, - aTargetChest.adjacentChestXNeg, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } else if (aTargetChest.adjacentChestZNeg != null) { - return moveMultipleItemStacks( - aTileEntity1, - aTargetChest.adjacentChestZNeg, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } else if (aTargetChest.adjacentChestXPos != null) { - return moveMultipleItemStacks( - aTileEntity1, - aTargetChest.adjacentChestXPos, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } else if (aTargetChest.adjacentChestZPos != null) { - return moveMultipleItemStacks( - aTileEntity1, - aTargetChest.adjacentChestZPos, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } - } - return 0; - } + // For each ItemStack, merge stacks from the end of the list to the front + slots.forEach((ignored, stacks) -> { + int stackLen = stacks.size(); - private static int moveFromAdjacentChests(TileEntityChest fromTileEntityChest, Object toObject, - ForgeDirection fromSide, ForgeDirection putSide, List aFilter, boolean aInvertFilter, - byte aMaxTargetStackSize, byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, - int aMaxStackTransfer) { - if (fromTileEntityChest.adjacentChestXNeg != null) { - return moveMultipleItemStacks( - fromTileEntityChest.adjacentChestXNeg, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } else if (fromTileEntityChest.adjacentChestZNeg != null) { - return moveMultipleItemStacks( - fromTileEntityChest.adjacentChestZNeg, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } else if (fromTileEntityChest.adjacentChestXPos != null) { - return moveMultipleItemStacks( - fromTileEntityChest.adjacentChestXPos, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } else if (fromTileEntityChest.adjacentChestZPos != null) { - return moveMultipleItemStacks( - fromTileEntityChest.adjacentChestZPos, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aMaxStackTransfer, - false); - } - return 0; - } + int insert = 0; + int extract = stackLen - 1; - /** - * Moves Stack from Inv-Side to Inv-Side. - * - * @return the Amount of moved Items - */ - public static byte moveOneItemStack(Object fromObject, Object toObject, ForgeDirection fromSide, - ForgeDirection putSide, List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, - byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce) { - if (fromObject instanceof IInventory inv) return moveOneItemStack( - inv, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - true); - return 0; - } + while (insert < stackLen && insert < extract) { + // Grab the next stack from the front of the list, to insert into if possible + var toInflate = stacks.get(insert); + ItemStack inflateStack = toInflate.left(); - /** - * This is only because I needed an additional Parameter for the Double Chest Check. - */ - private static byte moveOneItemStack(IInventory fromInventory, Object toObject, ForgeDirection fromSide, - ForgeDirection putSide, List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, - byte aMinTargetStackSize, byte aMaxMoveAtOnce, byte aMinMoveAtOnce, boolean aDoCheckChests) { - if (fromInventory == null || aMinTargetStackSize <= 0 - || aMaxMoveAtOnce <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMinMoveAtOnce > aMaxMoveAtOnce) return 0; - - int[] tGrabSlots = null; - if (fromInventory instanceof ISidedInventory) - tGrabSlots = ((ISidedInventory) fromInventory).getAccessibleSlotsFromSide(fromSide.ordinal()); - if (tGrabSlots == null) { - tGrabSlots = new int[fromInventory.getSizeInventory()]; - for (int i = 0; i < tGrabSlots.length; i++) tGrabSlots[i] = i; - } - - if (toObject instanceof IInventory inv) { - int[] tPutSlots = null; - if (toObject instanceof ISidedInventory sided) - tPutSlots = sided.getAccessibleSlotsFromSide(putSide.ordinal()); - - if (tPutSlots == null) { - tPutSlots = new int[inv.getSizeInventory()]; - for (int i = 0; i < tPutSlots.length; i++) tPutSlots[i] = i; - } + int maxStack = stackSizes.getSlotStackLimit(toInflate.rightInt(), inflateStack); + int remaining = maxStack - inflateStack.stackSize; - for (final int tGrabSlot : tGrabSlots) { - byte tMovedItemCount = 0; - final ItemStack tGrabStack = fromInventory.getStackInSlot(tGrabSlot); - if (listContainsItem(aFilter, tGrabStack, true, aInvertFilter) - && (tGrabStack.stackSize >= aMinMoveAtOnce - && isAllowedToTakeFromSlot(fromInventory, tGrabSlot, fromSide, tGrabStack))) { - for (final int tPutSlot : tPutSlots) { - if (isAllowedToPutIntoSlot(inv, tPutSlot, putSide, tGrabStack, aMaxTargetStackSize)) { - tMovedItemCount += moveStackFromSlotAToSlotB( - fromInventory, - inv, - tGrabSlot, - tPutSlot, - aMaxTargetStackSize, - aMinTargetStackSize, - (byte) (aMaxMoveAtOnce - tMovedItemCount), - aMinMoveAtOnce); - if (tMovedItemCount >= aMaxMoveAtOnce || (tMovedItemCount > 0 && aMaxTargetStackSize < 64)) - return tMovedItemCount; - } + // Scan from the end of the list to the current stack, and try to move items from those stacks into the + // current stack + while (insert < extract) { + var toBeExtracted = stacks.get(extract); + + int toTransfer = Math.min(toBeExtracted.left().stackSize, remaining); + + toBeExtracted.left().stackSize -= toTransfer; + inflateStack.stackSize += toTransfer; + remaining -= toTransfer; + + if (toBeExtracted.left().stackSize <= 0) { + inv.set(toBeExtracted.rightInt(), null); + extract--; } + if (inflateStack.stackSize >= maxStack) { + break; + } } - if (tMovedItemCount > 0) return tMovedItemCount; + + insert++; } + }); - if (aDoCheckChests && fromInventory instanceof TileEntityChest fromChest - && (fromChest.adjacentChestChecked)) { - byte tAmount = 0; - if (fromChest.adjacentChestXNeg != null) { - tAmount = moveOneItemStack( - fromChest.adjacentChestXNeg, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (fromChest.adjacentChestZNeg != null) { - tAmount = moveOneItemStack( - fromChest.adjacentChestZNeg, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (fromChest.adjacentChestXPos != null) { - tAmount = moveOneItemStack( - fromChest.adjacentChestXPos, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (fromChest.adjacentChestZPos != null) { - tAmount = moveOneItemStack( - fromChest.adjacentChestZPos, - toObject, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } - if (tAmount != 0) return tAmount; + int insert = 0; - } - if (aDoCheckChests && toObject instanceof TileEntityChest toChest && (toChest.adjacentChestChecked)) { - byte tAmount = 0; - if (toChest.adjacentChestXNeg != null) { - tAmount = moveOneItemStack( - fromInventory, - toChest.adjacentChestXNeg, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (toChest.adjacentChestZNeg != null) { - tAmount = moveOneItemStack( - fromInventory, - toChest.adjacentChestZNeg, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (toChest.adjacentChestXPos != null) { - tAmount = moveOneItemStack( - fromInventory, - toChest.adjacentChestXPos, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (toChest.adjacentChestZPos != null) { - tAmount = moveOneItemStack( - fromInventory, - toChest.adjacentChestZPos, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); + // Put all stacks into the first slots, contiguously + while (insert < len) { + if (inv.get(insert) == null) { + ItemStack stack = null; + + int extract = insert + 1; + + while (extract < len && (stack = inv.get(extract)) == null) { + extract++; } - if (tAmount != 0) return tAmount; + if (stack != null) { + inv.set(insert, stack); + inv.set(extract, null); + } else { + break; + } } + + insert++; } + } - return moveStackIntoPipe( - fromInventory, - toObject, - tGrabSlots, - fromSide, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aDoCheckChests); + public static void swapSlots(IInventory inv, int a, int b) { + ItemStack stackA = inv.getStackInSlot(a); + ItemStack stackB = inv.getStackInSlot(b); + + inv.setInventorySlotContents(a, stackB); + inv.setInventorySlotContents(b, stackA); + + inv.markDirty(); } - /** - * Moves Stack from Inv-Side to Inv-Slot. - * - * @return the Amount of moved Items - */ - public static byte moveOneItemStackIntoSlot(Object fromTileEntity, Object toTileEntity, ForgeDirection fromSide, - int putSlot, List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize, - byte aMaxMoveAtOnce, byte aMinMoveAtOnce) { - if (!(fromTileEntity instanceof IInventory fromInv) || aMinTargetStackSize <= 0 - || aMaxMoveAtOnce <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMinMoveAtOnce > aMaxMoveAtOnce) return 0; - - int[] tGrabSlots = null; - if (fromTileEntity instanceof ISidedInventory sided) - tGrabSlots = sided.getAccessibleSlotsFromSide(fromSide.ordinal()); - if (tGrabSlots == null) { - tGrabSlots = new int[fromInv.getSizeInventory()]; - for (int i = 0; i < tGrabSlots.length; i++) tGrabSlots[i] = i; - } - - if (toTileEntity instanceof IInventory toInv) { - for (final int tGrabSlot : tGrabSlots) { - if (listContainsItem(aFilter, fromInv.getStackInSlot(tGrabSlot), true, aInvertFilter)) { - if (isAllowedToTakeFromSlot(fromInv, tGrabSlot, fromSide, fromInv.getStackInSlot(tGrabSlot))) { - if (isAllowedToPutIntoSlot( - toInv, - putSlot, - ForgeDirection.UNKNOWN, - fromInv.getStackInSlot(tGrabSlot), - aMaxTargetStackSize)) { - byte tMovedItemCount = moveStackFromSlotAToSlotB( - fromInv, - toInv, - tGrabSlot, - putSlot, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce); - if (tMovedItemCount > 0) return tMovedItemCount; - } - } - } + public static void cleanInventory(IInventory inv) { + cleanInventory(wrapInventory(inv)); + } + + public static void cleanInventory(List inv) { + for (int i = 0, invSize = inv.size(); i < invSize; i++) { + ItemStack stack = inv.get(i); + + if (stack != null && (stack.getItem() == null || stack.stackSize <= 0)) { + inv.set(i, null); } } - - final ForgeDirection toSide = fromSide.getOpposite(); - moveStackIntoPipe( - fromInv, - toTileEntity, - tGrabSlots, - fromSide, - ForgeDirection.UNKNOWN, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce); - return 0; } - /** - * Moves Stack from Inv-Slot to Inv-Slot. - * - * @return the Amount of moved Items - */ - public static byte moveFromSlotToSlot(IInventory fromInv, IInventory toInv, int aGrabFrom, int aPutTo, - List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize, - byte aMaxMoveAtOnce, byte aMinMoveAtOnce) { - if (fromInv == null || toInv == null - || aGrabFrom < 0 - || aPutTo < 0 - || aMinTargetStackSize <= 0 - || aMaxMoveAtOnce <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMinMoveAtOnce > aMaxMoveAtOnce) return 0; - if (listContainsItem(aFilter, fromInv.getStackInSlot(aGrabFrom), true, aInvertFilter)) { - if (isAllowedToTakeFromSlot( - fromInv, - aGrabFrom, - ForgeDirection.UNKNOWN, - fromInv.getStackInSlot(aGrabFrom))) { - if (isAllowedToPutIntoSlot( - toInv, - aPutTo, - ForgeDirection.UNKNOWN, - fromInv.getStackInSlot(aGrabFrom), - aMaxTargetStackSize)) { - byte tMovedItemCount = moveStackFromSlotAToSlotB( - fromInv, - toInv, - aGrabFrom, - aPutTo, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce); - if (tMovedItemCount > 0) return tMovedItemCount; + public static void dropItemsOrClusters(World world, float x, float y, float z, List stacks) { + if (Mods.AvaritiaAddons.isModLoaded()) { + dropMatterClusters(world, x, y, z, stacks); + } else { + for (ItemStack stack : stacks) { + int maxStack = stack.getMaxStackSize(); + + while (stack.stackSize > 0) { + int inStack = Math.min(stack.stackSize, maxStack); + stack.stackSize -= inStack; + + EntityItem item = new EntityItem(world, x, y, z, GTUtility.copyAmountUnsafe(inStack, stack)); + + item.motionX = 0; + item.motionY = 0; + item.motionZ = 0; + + world.spawnEntityInWorld(item); } } } - return 0; } - /** - * Moves Stack from Inv-Side to Inv-Slot. - * - * @return the Amount of moved Items - */ - public static byte moveFromSlotToSide(IInventory fromTile, Object toTile, int fromSlot, ForgeDirection putSide, - List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize, - byte aMaxMoveAtOnce, byte aMinMoveAtOnce, boolean aDoCheckChests) { - if (fromTile == null || fromSlot < 0 - || aMinTargetStackSize <= 0 - || aMaxMoveAtOnce <= 0 - || aMinTargetStackSize > aMaxTargetStackSize - || aMinMoveAtOnce > aMaxMoveAtOnce) return 0; - - if (!listContainsItem(aFilter, fromTile.getStackInSlot(fromSlot), true, aInvertFilter) - || !isAllowedToTakeFromSlot(fromTile, fromSlot, ForgeDirection.UNKNOWN, fromTile.getStackInSlot(fromSlot))) - return 0; - - if (toTile instanceof IInventory) { - int[] tPutSlots = null; - if (toTile instanceof ISidedInventory sided) - tPutSlots = sided.getAccessibleSlotsFromSide(putSide.ordinal()); - - if (tPutSlots == null) { - tPutSlots = new int[((IInventory) toTile).getSizeInventory()]; - for (int i = 0; i < tPutSlots.length; i++) tPutSlots[i] = i; - } + @cpw.mods.fml.common.Optional.Method(modid = Mods.ModIDs.AVARITIA) + public static void dropMatterClusters(World world, float x, float y, float z, List stacks) { + for (ItemStack cluster : ItemMatterCluster.makeClusters(stacks)) { + EntityItem item = new EntityItem(world, x, y, z, cluster); - byte tMovedItemCount = 0; - for (final int tPutSlot : tPutSlots) { - if (isAllowedToPutIntoSlot( - (IInventory) toTile, - tPutSlot, - putSide, - fromTile.getStackInSlot(fromSlot), - aMaxTargetStackSize)) { - tMovedItemCount += moveStackFromSlotAToSlotB( - fromTile, - (IInventory) toTile, - fromSlot, - tPutSlot, - aMaxTargetStackSize, - aMinTargetStackSize, - (byte) (aMaxMoveAtOnce - tMovedItemCount), - aMinMoveAtOnce); - if (tMovedItemCount >= aMaxMoveAtOnce) { - return tMovedItemCount; - } - } - } - if (tMovedItemCount > 0) return tMovedItemCount; - - if (aDoCheckChests && toTile instanceof TileEntityChest tTileEntity2) { - if (tTileEntity2.adjacentChestChecked) { - if (tTileEntity2.adjacentChestXNeg != null) { - tMovedItemCount = moveFromSlotToSide( - fromTile, - tTileEntity2.adjacentChestXNeg, - fromSlot, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (tTileEntity2.adjacentChestZNeg != null) { - tMovedItemCount = moveFromSlotToSide( - fromTile, - tTileEntity2.adjacentChestZNeg, - fromSlot, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (tTileEntity2.adjacentChestXPos != null) { - tMovedItemCount = moveFromSlotToSide( - fromTile, - tTileEntity2.adjacentChestXPos, - fromSlot, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } else if (tTileEntity2.adjacentChestZPos != null) { - tMovedItemCount = moveFromSlotToSide( - fromTile, - tTileEntity2.adjacentChestZPos, - fromSlot, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - false); - } - if (tMovedItemCount > 0) return tMovedItemCount; - } - } + item.motionX = 0; + item.motionY = 0; + item.motionZ = 0; + + world.spawnEntityInWorld(item); } - return moveStackIntoPipe( - fromTile, - toTile, - new int[] { fromSlot }, - ForgeDirection.UNKNOWN, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - aDoCheckChests); - } - - public static byte moveFromSlotToSide(IInventory fromTile, Object toTile, int fromSlot, ForgeDirection putSide, - List aFilter, boolean aInvertFilter, byte aMaxTargetStackSize, byte aMinTargetStackSize, - byte aMaxMoveAtOnce, byte aMinMoveAtOnce) { - return moveFromSlotToSide( - fromTile, - toTile, - fromSlot, - putSide, - aFilter, - aInvertFilter, - aMaxTargetStackSize, - aMinTargetStackSize, - aMaxMoveAtOnce, - aMinMoveAtOnce, - true); } /** @@ -2126,6 +1216,12 @@ public static Object2LongOpenHashMap getItemStackHistogram(Iterable getCompoundTagList(NBTTagCompound tag, String name) { + NBTTagList list = tag.getTagList(name, Constants.NBT.TAG_COMPOUND); + + return list.tagList; + } + public static synchronized boolean removeIC2BottleRecipe(ItemStack aContainer, ItemStack aInput, Map aRecipeList, ItemStack aOutput) { if ((isStackInvalid(aInput) && isStackInvalid(aOutput) && isStackInvalid(aContainer)) || aRecipeList == null) @@ -4678,6 +3774,14 @@ public static double linearCurve(double x, double x1, double y1, double x2, doub return map(x, x1, x2, y1, y2); } + public static int min(int a, int b, int c) { + return Math.min(a, Math.min(b, c)); + } + + public static int min(int a, int b, int c, int d) { + return Math.min(a, Math.min(b, Math.min(c, b))); + } + public static int min(int first, int... rest) { for (int i = 0; i < rest.length; i++) { int l = rest[i]; @@ -4875,6 +3979,34 @@ public static long log4ceil(long a) { return 65 - Long.numberOfLeadingZeros(a - 1) >> 1; } + public static int addSafe(int a, int b) { + int result = a + b; + + if (a > 0 && b > 0 && result <= 0) { + return Integer.MAX_VALUE; + } + + if (a < 0 && b < 0 && result >= 0) { + return Integer.MIN_VALUE; + } + + return result; + } + + public static long addSafe(long a, long b) { + long result = a + b; + + if (a > 0 && b > 0 && result <= 0) { + return Long.MAX_VALUE; + } + + if (a < 0 && b < 0 && result >= 0) { + return Long.MIN_VALUE; + } + + return result; + } + /** * Hash an item stack for the purpose of storing hash across launches */ @@ -5195,15 +4327,15 @@ public static ItemId createNoCopy(Item item, int metaData, @Nullable NBTTagCompo return new AutoValue_GTUtility_ItemId(item, metaData, nbt, null); } - protected abstract Item item(); + public abstract Item item(); - protected abstract int metaData(); + public abstract int metaData(); @Nullable - protected abstract NBTTagCompound nbt(); + public abstract NBTTagCompound nbt(); @Nullable - protected abstract Integer stackSize(); + public abstract Integer stackSize(); public NBTTagCompound writeToNBT() { NBTTagCompound tag = new NBTTagCompound(); diff --git a/src/main/java/gregtech/common/covers/CoverArm.java b/src/main/java/gregtech/common/covers/CoverArm.java index d58be1c5a33..eb886666d52 100644 --- a/src/main/java/gregtech/common/covers/CoverArm.java +++ b/src/main/java/gregtech/common/covers/CoverArm.java @@ -3,12 +3,10 @@ import static net.minecraft.util.StatCollector.translateToLocal; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.IInventory; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagInt; import net.minecraft.tileentity.TileEntity; -import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import org.jetbrains.annotations.NotNull; @@ -21,6 +19,7 @@ import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.tileentity.ICoverable; import gregtech.api.interfaces.tileentity.IMachineProgress; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; import gregtech.common.covers.gui.CoverArmGui; import gregtech.common.covers.gui.CoverGui; @@ -53,77 +52,27 @@ public void doCoverThings(byte aInputRedstone, long aTimer) { return; } - final TileEntity toTile; - final TileEntity fromTile; + GTItemTransfer transfer = new GTItemTransfer(); + final int toSlot; final int fromSlot; if (export) { - fromTile = tileEntity; - toTile = coverable.getTileEntityAtSide(coverSide); + transfer.push(coverable, coverSide); + fromSlot = internalSlotId; toSlot = externalSlotId; } else { - fromTile = coverable.getTileEntityAtSide(coverSide); - toTile = tileEntity; + transfer.pull(coverable, coverSide); + fromSlot = externalSlotId; toSlot = internalSlotId; } - if (fromSlot > 0 && toSlot > 0) { - if (fromTile instanceof IInventory fromInventory && toTile instanceof IInventory toInventory) - GTUtility.moveFromSlotToSlot( - fromInventory, - toInventory, - fromSlot - 1, - toSlot - 1, - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } else if (toSlot > 0) { - final ForgeDirection toSide = export ? coverSide : coverSide.getOpposite(); - GTUtility.moveOneItemStackIntoSlot( - fromTile, - toTile, - toSide, - toSlot - 1, - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } else if (fromSlot > 0) { - final ForgeDirection toSide = export ? coverSide : coverSide.getOpposite(); - if (fromTile instanceof IInventory fromInventory) GTUtility.moveFromSlotToSide( - fromInventory, - toTile, - fromSlot - 1, - toSide, - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } else { - final ForgeDirection fromSide = export ? coverSide : coverSide.getOpposite(); - final ForgeDirection toSide = fromSide.getOpposite(); - GTUtility.moveOneItemStack( - fromTile, - toTile, - fromSide, - toSide, - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } + if (fromSlot > 0) transfer.setSourceSlots(fromSlot - 1); + if (toSlot > 0) transfer.setSinkSlots(toSlot - 1); + + transfer.transfer(); } @Override diff --git a/src/main/java/gregtech/common/covers/CoverConveyor.java b/src/main/java/gregtech/common/covers/CoverConveyor.java index cda1251890e..d2b7dba1915 100644 --- a/src/main/java/gregtech/common/covers/CoverConveyor.java +++ b/src/main/java/gregtech/common/covers/CoverConveyor.java @@ -1,9 +1,5 @@ package gregtech.common.covers; -import static gregtech.api.util.GTUtility.moveMultipleItemStacks; - -import net.minecraft.tileentity.TileEntity; -import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import org.jetbrains.annotations.NotNull; @@ -14,6 +10,7 @@ import gregtech.api.gui.modularui.CoverUIBuildContext; import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.util.GTItemTransfer; import gregtech.common.covers.gui.CoverGui; import gregtech.common.covers.gui.CoverIOBaseGui; import gregtech.common.gui.mui1.cover.ConveyorUIFactory; @@ -36,24 +33,21 @@ public boolean isRedstoneSensitive(long aTimer) { @Override protected void doTransfer(ICoverable coverable) { - final TileEntity tTileEntity = coverable.getTileEntityAtSide(coverSide); - final Object fromEntity = this.coverData % 2 == 0 ? coverable : tTileEntity; - final Object toEntity = this.coverData % 2 != 0 ? coverable : tTileEntity; - final ForgeDirection fromSide = this.coverData % 2 != 0 ? coverSide.getOpposite() : coverSide; - final ForgeDirection toSide = this.coverData % 2 == 0 ? coverSide.getOpposite() : coverSide; - - moveMultipleItemStacks( - fromEntity, - toEntity, - fromSide, - toSide, - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1, - this.mMaxStacks); + GTItemTransfer transfer = new GTItemTransfer(); + + switch (getIOMode()) { + case EXPORT -> { + transfer.push(coverable, coverSide, coverable.getTileEntityAtSide(coverSide)); + } + case IMPORT -> { + transfer.pull(coverable, coverSide, coverable.getTileEntityAtSide(coverSide)); + } + } + + transfer.setStacksToTransfer(mMaxStacks); + transfer.dropItems(coverable, coverSide); + + transfer.transfer(); } @Override diff --git a/src/main/java/gregtech/common/covers/CoverItemFilter.java b/src/main/java/gregtech/common/covers/CoverItemFilter.java index 63f1dadd513..c6f498bde1c 100644 --- a/src/main/java/gregtech/common/covers/CoverItemFilter.java +++ b/src/main/java/gregtech/common/covers/CoverItemFilter.java @@ -1,23 +1,17 @@ package gregtech.common.covers; -import static gregtech.api.util.GTUtility.moveMultipleItemStacks; - -import java.util.Collections; -import java.util.List; - import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.Constants; -import net.minecraftforge.common.util.ForgeDirection; import org.jetbrains.annotations.NotNull; import com.cleanroommc.modularui.utils.item.ItemStackHandler; import com.cleanroommc.modularui.utils.item.LimitingItemStackHandler; import com.google.common.io.ByteArrayDataInput; +import com.gtnewhorizon.gtnhlib.capability.item.ItemStackPredicate; import com.gtnewhorizons.modularui.api.screen.ModularWindow; import cpw.mods.fml.common.network.ByteBufUtils; @@ -26,6 +20,7 @@ import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.tileentity.ICoverable; import gregtech.api.util.GTByteBuffer; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; import gregtech.common.covers.gui.CoverGui; import gregtech.common.covers.gui.CoverItemFilterGui; @@ -118,26 +113,20 @@ public void doCoverThings(byte aInputRedstone, long aTimer) { if (coverable == null) { return; } - final TileEntity tTileEntity = coverable.getTileEntityAtSide(coverSide); - final Object fromEntity = mExport ? coverable : tTileEntity; - final Object toEntity = !mExport ? coverable : tTileEntity; - final ForgeDirection fromSide = !mExport ? coverSide.getOpposite() : coverSide; - final ForgeDirection toSide = mExport ? coverSide.getOpposite() : coverSide; - - final List filter = Collections.singletonList(this.filter.getStackInSlot(0)); - - moveMultipleItemStacks( - fromEntity, - toEntity, - fromSide, - toSide, - filter, - mWhitelist, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1, - 64); + + GTItemTransfer transfer = new GTItemTransfer(); + + if (mExport) { + transfer.push(coverable, coverSide); + } else { + transfer.pull(coverable, coverSide); + } + + transfer.setFilter(ItemStackPredicate.matches(this.filter.getStackInSlot(0))); + + transfer.setStacksToTransfer(64); + + transfer.transfer(); } @Override diff --git a/src/main/java/gregtech/common/gui/modularui/util/ProxiedItemHandlerModifiable.java b/src/main/java/gregtech/common/gui/modularui/util/ProxiedItemHandlerModifiable.java new file mode 100644 index 00000000000..e5abfc7aadb --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/util/ProxiedItemHandlerModifiable.java @@ -0,0 +1,46 @@ +package gregtech.common.gui.modularui.util; + +import net.minecraft.item.ItemStack; + +import org.jetbrains.annotations.Nullable; + +import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; + +public class ProxiedItemHandlerModifiable implements IItemHandlerModifiable { + + public final IItemHandlerModifiable proxied; + + public ProxiedItemHandlerModifiable(IItemHandlerModifiable proxied) { + this.proxied = proxied; + } + + @Override + public void setStackInSlot(int slot, @Nullable ItemStack stack) { + proxied.setStackInSlot(slot, stack); + } + + @Override + public int getSlots() { + return proxied.getSlots(); + } + + @Override + public @Nullable ItemStack getStackInSlot(int slot) { + return proxied.getStackInSlot(slot); + } + + @Override + public @Nullable ItemStack insertItem(int slot, @Nullable ItemStack stack, boolean simulate) { + return proxied.insertItem(slot, stack, simulate); + } + + @Override + public @Nullable ItemStack extractItem(int slot, int amount, boolean simulate) { + return proxied.extractItem(slot, amount, simulate); + } + + @Override + public int getSlotLimit(int slot) { + return proxied.getSlotLimit(slot); + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/AEBaseSlot.java b/src/main/java/gregtech/common/gui/modularui/widget/AEBaseSlot.java new file mode 100644 index 00000000000..511a41ef8cd --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/widget/AEBaseSlot.java @@ -0,0 +1,24 @@ +package gregtech.common.gui.modularui.widget; + +import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; + +import appeng.api.storage.data.IAEItemStack; + +public class AEBaseSlot extends BaseSlot { + + private final IAEItemHandlerModifiable inventory; + + public AEBaseSlot(IAEItemHandlerModifiable inventory, int index, boolean phantom) { + super(inventory, index, phantom); + this.inventory = inventory; + } + + public AEBaseSlot(IAEItemHandlerModifiable inventory, int index) { + super(inventory, index); + this.inventory = inventory; + } + + public IAEItemStack getAEStack() { + return inventory.getAEStackInSlot(getSlotIndex()); + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java b/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java index 368f219ef44..504c7a98781 100644 --- a/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java +++ b/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java @@ -6,16 +6,21 @@ import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +import com.gtnewhorizons.modularui.api.NumberFormatMUI; import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; import com.gtnewhorizons.modularui.common.widget.SlotWidget; import appeng.api.storage.IItemDisplayRegistry.ItemRenderHook; +import appeng.api.storage.data.IAEItemStack; import appeng.client.render.AppEngRenderItem; import appeng.core.AELog; import appeng.util.Platform; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.util.GTUtility; public class AESlotWidget extends SlotWidget { @@ -39,6 +44,14 @@ public AESlotWidget(BaseSlot slot) { super(slot); } + public IAEItemStack getAEStack() { + if (getMcSlot() instanceof AEBaseSlot aeSlot) { + return aeSlot.getAEStack(); + } else { + return Platform.getAEStackInSlot(getMcSlot()); + } + } + @Override @SideOnly(Side.CLIENT) protected void drawSlot(Slot slotIn) { @@ -46,7 +59,8 @@ protected void drawSlot(Slot slotIn) { AppEngRenderItem.POST_HOOKS.add(HookHolder.SKIP_ITEM_STACK_SIZE_HOOK); final RenderItem pIR = this.setItemRender(aeRenderItem); try { - aeRenderItem.setAeStack(Platform.getAEStackInSlot(slotIn)); + aeRenderItem.setAeStack(getAEStack()); + super.drawSlot(slotIn, true); } catch (final Exception err) { AELog.warn("[AppEng] AE prevented crash while drawing slot: " + err); @@ -61,4 +75,18 @@ private RenderItem setItemRender(final RenderItem item) { ModularGui.setItemRenderer(item); return ri; } + + private static final NumberFormatMUI numberFormat = new NumberFormatMUI(); + + @Override + protected @NotNull String getAmountText(int amount, String format) { + return numberFormat + .formatWithSuffix(getAEStack().getStackSize(), new StringBuffer(format == null ? "" : format)) + .toString(); + } + + @Override + protected String getAmountTooltip() { + return GTUtility.translate("modularui.amount", getAEStack().getStackSize()); + } } diff --git a/src/main/java/gregtech/common/gui/modularui/widget/IAEItemHandlerModifiable.java b/src/main/java/gregtech/common/gui/modularui/widget/IAEItemHandlerModifiable.java new file mode 100644 index 00000000000..2696ec4a9b0 --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/widget/IAEItemHandlerModifiable.java @@ -0,0 +1,88 @@ +package gregtech.common.gui.modularui.widget; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; + +import org.jetbrains.annotations.NotNull; + +import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; + +import appeng.api.storage.data.IAEItemStack; +import appeng.util.item.AEItemStack; +import gregtech.api.util.GTUtility; +import gregtech.common.inventory.IAEInventory; + +/** + * A version of {@link IItemHandlerModifiable} that supports AE stacks (stacks bigger than int max). + * By default all MC stack operations proxy to the AE method equivalents and truncate any stack sizes >int max. + */ +public interface IAEItemHandlerModifiable extends IItemHandlerModifiable, IAEInventory { + + IAEItemStack getAEStackInSlot(int slot); + + @Override + default ItemStack getStackInSlot(int slot) { + IAEItemStack stack = getAEStackInSlot(slot); + + return stack == null ? null : stack.getItemStack(); + } + + @Nullable + IAEItemStack insertAEItem(int slot, @NotNull IAEItemStack stack, boolean simulate); + + @Override + @Nullable + default ItemStack insertItem(int slot, @Nullable ItemStack stack, boolean simulate) { + if (!GTUtility.isStackValid(stack)) return null; + + IAEItemStack rejected = insertAEItem(slot, AEItemStack.create(stack), simulate); + + return rejected == null ? null : rejected.getItemStack(); + } + + @Nullable + IAEItemStack extractAEItem(int slot, long amount, boolean simulate); + + @Override + @Nullable + default ItemStack extractItem(int slot, int amount, boolean simulate) { + IAEItemStack stack = extractAEItem(slot, amount, simulate); + + return stack == null ? null : stack.getItemStack(); + } + + default long getAESlotLimit(int slot) { + return getAESlotLimit(slot, getAEStackInSlot(slot)); + } + + long getAESlotLimit(int slot, IAEItemStack stack); + + @Override + default int getSlotLimit(int slot) { + return GTUtility.longToInt(getAESlotLimit(slot)); + } + + @Override + default boolean isItemValid(int slot, ItemStack stack) { + return isAEItemValid(slot, AEItemStack.create(stack)); + } + + default List getAEStacks() { + List ret = new ArrayList<>(); + for (int i = 0; i < getSlots(); i++) { + ret.add(getAEStackInSlot(i)); + } + return ret; + } + + void setStackInSlot(int slot, IAEItemStack stack); + + @Override + default void setStackInSlot(int slot, ItemStack stack) { + setStackInSlot(slot, AEItemStack.create(stack)); + } +} diff --git a/src/main/java/gregtech/common/inventory/AEInventory.java b/src/main/java/gregtech/common/inventory/AEInventory.java new file mode 100644 index 00000000000..7ab156688a3 --- /dev/null +++ b/src/main/java/gregtech/common/inventory/AEInventory.java @@ -0,0 +1,435 @@ +package gregtech.common.inventory; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import appeng.api.config.AccessRestriction; +import appeng.api.config.Actionable; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.IMEMonitorHandlerReceiver; +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.util.item.AEItemStack; +import appeng.util.item.ItemList; +import gregtech.api.util.GTDataUtils; +import gregtech.api.util.GTUtility; +import gregtech.common.gui.modularui.widget.IAEItemHandlerModifiable; + +/** + * An inventory that stores AE item stacks. This also sends monitor updates as needed. + */ +public abstract class AEInventory implements IAEInventory, IMEMonitor, IAEItemHandlerModifiable { + + private final Map, Object> listeners = new HashMap<>(); + + public final int slotCount; + public final IAEItemStack[] inventory; + + protected final int[] allSlots; + + public AEInventory(IAEItemStack[] inventory) { + this.inventory = inventory; + slotCount = inventory.length; + allSlots = IntStream.range(0, slotCount) + .toArray(); + } + + public AEInventory(int slotCount) { + this.inventory = new IAEItemStack[slotCount]; + this.slotCount = slotCount; + allSlots = IntStream.range(0, slotCount) + .toArray(); + } + + @Override + public IItemList getStorageList() { + ItemList list = new ItemList(); + + for (IAEItemStack stack : inventory) { + if (stack != null) list.add(stack.copy()); + } + + return list; + } + + @Override + public void addListener(IMEMonitorHandlerReceiver l, Object verificationToken) { + listeners.put(l, verificationToken); + } + + @Override + public void removeListener(IMEMonitorHandlerReceiver l) { + listeners.remove(l); + } + + @Override + public AccessRestriction getAccess() { + return AccessRestriction.READ_WRITE; + } + + @Override + public boolean isPrioritized(IAEItemStack input) { + return false; + } + + @Override + public boolean canAccept(IAEItemStack input) { + return true; + } + + @Override + public int getPriority() { + return 0; + } + + @Override + public int getSlot() { + return 0; + } + + @Override + public boolean validForPass(int i) { + return false; + } + + protected int[] getValidInjectionSlots(IAEItemStack input) { + return allSlots; + } + + protected int[] getValidExtractionSlots(IAEItemStack request) { + return allSlots; + } + + protected boolean allowPullStack(int slotIndex) { + return true; + } + + protected boolean allowPutStack(int slotIndex, IAEItemStack toInsert) { + return true; + } + + @Override + public IAEItemStack injectItems(IAEItemStack input, Actionable mode, BaseActionSource src) { + return injectItems(input, mode, src, getValidInjectionSlots(input)); + } + + public @Nullable IAEItemStack injectItems(IAEItemStack input, Actionable mode, BaseActionSource src, int[] slots) { + IAEItemStack inserted = null; + + input = input.copy(); + + // First pass: try to find slots that have the item already + for (int slotIndex : slots) { + if (input.getStackSize() <= 0) break; + + if (!allowPutStack(slotIndex, input)) continue; + + IAEItemStack slot = inventory[slotIndex]; + + if (slot == null) continue; + if (!input.isSameType(slot)) continue; + + long maxStack = getAESlotLimit(slotIndex, slot); + + long toTransfer = Math.min(maxStack - slot.getStackSize(), input.getStackSize()); + + if (toTransfer > 0) { + input.decStackSize(toTransfer); + if (mode == Actionable.MODULATE) slot.incStackSize(toTransfer); + + if (inserted == null) { + inserted = input.copy() + .setStackSize(0); + } + + inserted.incStackSize(toTransfer); + } + } + + // Second pass: try to find any empty slot + for (int slotIndex : slots) { + if (input.getStackSize() <= 0) break; + + if (!allowPutStack(slotIndex, input)) continue; + + IAEItemStack slot = inventory[slotIndex]; + + if (slot != null) continue; + + slot = input.empty(); + + if (mode == Actionable.MODULATE) inventory[slotIndex] = slot; + + long maxStack = getAESlotLimit(slotIndex, slot); + + long toTransfer = Math.min(maxStack - slot.getStackSize(), input.getStackSize()); + + if (toTransfer > 0) { + input.decStackSize(toTransfer); + if (mode == Actionable.MODULATE) slot.incStackSize(toTransfer); + + if (inserted == null) { + inserted = input.empty(); + } + + inserted.incStackSize(toTransfer); + } + } + + if (inserted != null && mode == Actionable.MODULATE) { + postChange(src, inserted); + } + + return input; + } + + private void postChange(BaseActionSource src, IAEItemStack inserted) { + Iterable changes = GTDataUtils.singletonIterable(inserted); + + listeners.forEach((handler, o) -> { + if (handler.isValid(o)) { + handler.postChange(this, changes, src); + } + }); + } + + @Override + public IAEItemStack extractItems(IAEItemStack request, Actionable mode, BaseActionSource src) { + return extractItems(request, mode, src, getValidExtractionSlots(request)); + } + + private @Nullable IAEItemStack extractItems(IAEItemStack request, Actionable mode, BaseActionSource src, + int[] slots) { + IAEItemStack extracted = null; + + for (int slotIndex : slots) { + if (!allowPullStack(slotIndex)) continue; + + IAEItemStack slot = inventory[slotIndex]; + + if (slot == null || !request.isSameType(slot)) continue; + + long toConsume = Math.min(slot.getStackSize(), request.getStackSize()); + + if (mode == Actionable.MODULATE) { + slot.decStackSize(toConsume); + + if (slot.getStackSize() <= 0) { + inventory[slotIndex] = null; + } + } + + if (extracted == null) { + extracted = slot.empty(); + } + + extracted.incStackSize(toConsume); + } + + if (extracted != null && mode == Actionable.MODULATE) { + postChange( + src, + extracted.empty() + .setStackSize(-extracted.getStackSize())); + } + + return extracted; + } + + @Override + public StorageChannel getChannel() { + return StorageChannel.ITEMS; + } + + @Override + public int getSlots() { + return slotCount; + } + + @Override + public @Nullable IAEItemStack getAEStackInSlot(int slot) { + return GTDataUtils.getIndexSafe(inventory, slot); + } + + public int indexOf(ItemStack stack) { + for (int i = 0; i < inventory.length; i++) { + IAEItemStack slot = inventory[i]; + + if (slot != null && slot.isSameType(stack)) return i; + } + + return -1; + } + + public int indexOf(Predicate filter) { + for (int i = 0; i < inventory.length; i++) { + if (filter.test(inventory[i])) return i; + } + + return -1; + } + + @Override + public @Nullable IAEItemStack insertAEItem(int slot, @NotNull IAEItemStack stack, boolean simulate) { + if (slot < 0 || slot >= slotCount) return stack; + + IAEItemStack existing = inventory[slot]; + + if (existing == null) { + existing = stack.empty(); + } + + if (simulate) { + existing = existing.copy(); + } + + if (existing.isSameType(stack)) { + long maxStorable = getAESlotLimit(slot, existing); + + long toTransfer = Math.min(maxStorable - existing.getStackSize(), stack.getStackSize()); + + if (toTransfer > 0) { + existing.incStackSize(toTransfer); + stack.decStackSize(toTransfer); + + if (!simulate) { + inventory[slot] = existing; + + postChange( + getActionSource(), + existing.empty() + .setStackSize(toTransfer)); + } + } + } + + return stack.getStackSize() <= 0 ? stack : null; + } + + @Override + public @Nullable IAEItemStack extractAEItem(int slot, long amount, boolean simulate) { + if (slot < 0 || slot >= slotCount) return null; + + IAEItemStack existing = inventory[slot]; + + if (existing == null) return null; + + long toExtract = Math.min(existing.getStackSize(), amount); + + IAEItemStack extracted = existing.empty() + .setStackSize(toExtract); + + if (!simulate) { + existing.decStackSize(toExtract); + + if (existing.getStackSize() <= 0) { + inventory[slot] = null; + } + + postChange( + getActionSource(), + existing.empty() + .setStackSize(-toExtract)); + } + + return extracted; + } + + @Override + public long getAESlotLimit(int slot, @Nullable IAEItemStack stack) { + return Long.MAX_VALUE; + } + + @Override + public void setStackInSlot(int slot, @Nullable IAEItemStack stack) { + if (slot < 0 || slot >= slotCount) return; + + IAEItemStack existing = inventory[slot]; + + inventory[slot] = stack != null ? stack.copy() : null; + + if (existing != null && !existing.isSameType(stack)) { + postChange( + getActionSource(), + existing.empty() + .setStackSize(-existing.getStackSize())); + existing = null; + } + + if (stack != null) { + long previousAmount = existing == null ? 0 : existing.getStackSize(); + + long delta = stack.getStackSize() - previousAmount; + + postChange( + getActionSource(), + stack.empty() + .setStackSize(delta)); + } + } + + public NBTTagCompound writeToNBT(NBTTagCompound tag) { + NBTTagList inv = new NBTTagList(); + tag.setTag("inv", inv); + + for (int i = 0; i < inventory.length; i++) { + IAEItemStack stack = inventory[i]; + + if (stack == null) continue; + + NBTTagCompound item = new NBTTagCompound(); + + stack.writeToNBT(item); + + item.setInteger("index", i); + + inv.appendTag(item); + } + + return tag; + } + + public void readFromNBT(NBTTagCompound tag) { + Arrays.fill(inventory, null); + + for (NBTTagCompound item : GTUtility.getCompoundTagList(tag, "inv")) { + int index = item.getInteger("index"); + + if (index < 0 || index >= slotCount) continue; + + inventory[index] = AEItemStack.loadItemStackFromNBT(item); + } + } + + protected abstract AEInventory copyImpl(); + + public AEInventory copy() { + AEInventory copy = copyImpl(); + + for (int i = 0; i < inventory.length; i++) { + IAEItemStack stack = inventory[i]; + + copy.inventory[i] = stack == null ? null : stack.copy(); + } + + return copy; + } + + protected abstract BaseActionSource getActionSource(); + + public AEInventoryItemIO getItemIO() { + return new AEInventoryItemIO(this, getActionSource()); + } +} diff --git a/src/main/java/gregtech/common/inventory/AEInventoryItemIO.java b/src/main/java/gregtech/common/inventory/AEInventoryItemIO.java new file mode 100644 index 00000000000..9a2d7d127b7 --- /dev/null +++ b/src/main/java/gregtech/common/inventory/AEInventoryItemIO.java @@ -0,0 +1,129 @@ +package gregtech.common.inventory; + +import java.util.OptionalInt; + +import net.minecraft.item.ItemStack; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.gtnewhorizon.gtnhlib.capability.item.AbstractInventorySourceIterator; +import com.gtnewhorizon.gtnhlib.capability.item.IItemIO; +import com.gtnewhorizon.gtnhlib.capability.item.InventorySourceIterator; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.storage.data.IAEItemStack; +import appeng.util.item.AEItemStack; +import gregtech.api.util.GTDataUtils; +import gregtech.api.util.GTUtility; + +public class AEInventoryItemIO implements IItemIO { + + private final AEInventory inventory; + private final BaseActionSource src; + + private int[] allowedSourceSlots, allowedSinkSlots; + + public AEInventoryItemIO(AEInventory inventory, BaseActionSource src) { + this.inventory = inventory; + this.src = src; + } + + @Override + public @NotNull InventorySourceIterator iterator() { + int[] effectiveSlots = allowedSourceSlots != null + ? GTDataUtils.intersect(allowedSourceSlots, inventory.allSlots) + : inventory.allSlots; + + return new AbstractInventorySourceIterator(effectiveSlots) { + + @Override + protected ItemStack getStackInSlot(int slot) { + return inventory.getStackInSlot(slot); + } + + @Override + public ItemStack extract(int amount) { + IAEItemStack inSlot = inventory.getAEStackInSlot(getCurrentSlot()); + + if (inSlot == null) return null; + + long toExtract = Math.min(inSlot.getStackSize(), amount); + + IAEItemStack extracted = inSlot.copy() + .setStackSize(toExtract); + inSlot.decStackSize(toExtract); + + inventory.setStackInSlot(getCurrentSlot(), inSlot.getStackSize() == 0 ? null : inSlot); + + return extracted.getItemStack(); + } + + @Override + public void insert(ItemStack stack) { + if (!GTUtility.isStackValid(stack) || stack.stackSize <= 0) return; + + IAEItemStack inSlot = inventory.getAEStackInSlot(getCurrentSlot()); + + if (inSlot != null && !inSlot.isSameType(stack)) { + throw new IllegalArgumentException( + "Cannot insert stack that does not match the existing stack. Attempted to inject: " + stack + + ", already had: " + + inSlot); + } + + IAEItemStack out; + + if (inSlot != null) { + out = inSlot.copy(); + out.incStackSize(stack.stackSize); + } else { + out = AEItemStack.create(stack); + } + + inventory.setStackInSlot(getCurrentSlot(), out); + } + + @Override + protected void setInventorySlotContents(int slot, ItemStack stack) { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public void setAllowedSourceSlots(int @Nullable [] slots) { + allowedSourceSlots = slots; + } + + @Override + public ItemStack store(ItemStack itemStack) { + IAEItemStack input = AEItemStack.create(itemStack); + + int[] slots = allowedSinkSlots != null ? GTDataUtils.intersect(allowedSinkSlots, inventory.allSlots) + : inventory.allSlots; + + IAEItemStack rejected = inventory.injectItems(input, Actionable.MODULATE, src, slots); + + return rejected == null ? null : rejected.getItemStack(); + } + + @Override + public void setAllowedSinkSlots(int @Nullable [] slots) { + allowedSinkSlots = slots; + } + + @Override + public OptionalInt getStoredAmount(@Nullable ItemStack query) { + long sum = 0; + + for (IAEItemStack stack : inventory.inventory) { + if (stack != null && stack.isSameType(query)) { + sum = GTUtility.addSafe(sum, stack.getStackSize()); + } + } + + return OptionalInt.of(GTUtility.longToInt(sum)); + } +} diff --git a/src/main/java/gregtech/common/inventory/IAEInventory.java b/src/main/java/gregtech/common/inventory/IAEInventory.java new file mode 100644 index 00000000000..3e4fc358fbf --- /dev/null +++ b/src/main/java/gregtech/common/inventory/IAEInventory.java @@ -0,0 +1,28 @@ +package gregtech.common.inventory; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import appeng.api.storage.data.IAEItemStack; + +public interface IAEInventory { + + int getSlots(); + + @Nullable + IAEItemStack getAEStackInSlot(int slot); + + @Nullable + IAEItemStack insertAEItem(int slot, @NotNull IAEItemStack stack, boolean simulate); + + @Nullable + IAEItemStack extractAEItem(int slot, long amount, boolean simulate); + + default boolean isAEItemValid(int slot, @NotNull IAEItemStack stack) { + return true; + } + + long getAESlotLimit(int slot, @Nullable IAEItemStack stack); + + void setStackInSlot(int slot, @Nullable IAEItemStack stack); +} diff --git a/src/main/java/gregtech/common/tileentities/automation/MTEItemDistributor.java b/src/main/java/gregtech/common/tileentities/automation/MTEItemDistributor.java index db7ca178203..21c39016c57 100644 --- a/src/main/java/gregtech/common/tileentities/automation/MTEItemDistributor.java +++ b/src/main/java/gregtech/common/tileentities/automation/MTEItemDistributor.java @@ -20,6 +20,7 @@ import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.metatileentity.implementations.MTEBuffer; import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; public class MTEItemDistributor extends MTEBuffer { @@ -114,11 +115,15 @@ public void loadNBTData(NBTTagCompound aNBT) { @Override protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { + if (aBaseMetaTileEntity.hasInventoryBeenModified()) { + GTUtility.compactInventory(this); + } + int currentSideOrdinal = currentSide.ordinal(); - fillStacksIntoFirstSlots(); - int movedItems; + TileEntity adjacentTileEntity = aBaseMetaTileEntity.getTileEntityAtSide(currentSide); int inspectedSides = 0; + while (itemsPerSide[currentSideOrdinal] == 0) { currentSideOrdinal = ((currentSideOrdinal + 1) % 6); currentSide = ForgeDirection.getOrientation(currentSideOrdinal); @@ -129,27 +134,27 @@ protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { return; } } - movedItems = GTUtility.moveOneItemStack( - aBaseMetaTileEntity, - adjacentTileEntity, - currentSide, - currentSide.getOpposite(), - null, - false, - (byte) 64, - (byte) 1, - (byte) (itemsPerSide[currentSideOrdinal] - currentSideItemCount), - (byte) 1); - currentSideItemCount += movedItems; + + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.source(aBaseMetaTileEntity, currentSide); + transfer.sink(adjacentTileEntity, currentSide.getOpposite()); + + transfer.setMaxItemsPerTransfer(itemsPerSide[currentSideOrdinal] - currentSideItemCount); + + int movedItems = transfer.transfer(); + currentSideItemCount += (byte) movedItems; + if (currentSideItemCount >= itemsPerSide[currentSideOrdinal]) { currentSideOrdinal = ((currentSideOrdinal + 1) % 6); currentSide = ForgeDirection.getOrientation(currentSideOrdinal); currentSideItemCount = 0; } + if (movedItems > 0 || aBaseMetaTileEntity.hasInventoryBeenModified()) { mSuccess = 50; + GTUtility.compactInventory(this); } - fillStacksIntoFirstSlots(); } @Override diff --git a/src/main/java/gregtech/common/tileentities/automation/MTERegulator.java b/src/main/java/gregtech/common/tileentities/automation/MTERegulator.java index 6f593d41d7e..f381a737b3c 100644 --- a/src/main/java/gregtech/common/tileentities/automation/MTERegulator.java +++ b/src/main/java/gregtech/common/tileentities/automation/MTERegulator.java @@ -3,13 +3,12 @@ import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_REGULATOR; import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_REGULATOR_GLOW; -import java.util.Collections; - import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.util.ForgeDirection; +import com.gtnewhorizon.gtnhlib.capability.item.ItemStackPredicate; import com.gtnewhorizons.modularui.api.screen.ModularWindow; import com.gtnewhorizons.modularui.api.screen.UIBuildContext; import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; @@ -24,6 +23,7 @@ import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.metatileentity.implementations.MTEBuffer; import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; public class MTERegulator extends MTEBuffer { @@ -101,21 +101,25 @@ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, f } @Override - public void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { - for (int i = 0, tCosts; i < 9; i++) { - if (this.mInventory[(i + 9)] != null) { - tCosts = GTUtility.moveOneItemStackIntoSlot( - getBaseMetaTileEntity(), - getBaseMetaTileEntity().getTileEntityAtSide(getBaseMetaTileEntity().getBackFacing()), - getBaseMetaTileEntity().getBackFacing(), - this.mTargetSlots[i], - Collections.singletonList(this.mInventory[(i + 9)]), - false, - (byte) this.mInventory[(i + 9)].stackSize, - (byte) this.mInventory[(i + 9)].stackSize, - (byte) 64, - (byte) 1) * 3; - if (tCosts > 0) { + public void moveItems(IGregTechTileEntity igte, long aTimer) { + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.source(igte, ForgeDirection.UNKNOWN); + transfer.sink(igte.getTileEntityAtSide(igte.getBackFacing()), igte.getFrontFacing()); + + for (int i = 0; i < 9; i++) { + if (this.mInventory[i + 9] != null) { + transfer.setSourceSlots(i); + transfer.setSinkSlots(this.mTargetSlots[i]); + + int size = this.mInventory[i + 9].stackSize; + + transfer.setMaxItemsPerTransfer(size); + transfer.setFilter( + ItemStackPredicate.matches(this.mInventory[i + 9]) + .and(stack -> stack.stackSize >= size)); + + if (transfer.transfer() > 0) { this.mSuccess = 50; break; } diff --git a/src/main/java/gregtech/common/tileentities/automation/MTESuperBuffer.java b/src/main/java/gregtech/common/tileentities/automation/MTESuperBuffer.java index 6dbf37898e8..e2675afe9b6 100644 --- a/src/main/java/gregtech/common/tileentities/automation/MTESuperBuffer.java +++ b/src/main/java/gregtech/common/tileentities/automation/MTESuperBuffer.java @@ -3,13 +3,6 @@ import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_SUPERBUFFER; import static gregtech.api.enums.Textures.BlockIcons.AUTOMATION_SUPERBUFFER_GLOW; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.minecraft.item.ItemStack; - import com.gtnewhorizons.modularui.api.screen.ModularWindow; import com.gtnewhorizons.modularui.common.widget.DrawableWidget; @@ -18,7 +11,6 @@ import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.render.TextureFactory; -import gregtech.api.util.GTUtility; public class MTESuperBuffer extends MTEChestBuffer { @@ -57,38 +49,6 @@ public ITexture getOverlayIcon() { .build()); } - @Override - protected void fillStacksIntoFirstSlots() { - // no order, this is super buffer - HashMap slots = new HashMap<>(mInventory.length); - HashMap stacks = new HashMap<>(mInventory.length); - List validSlots = new ArrayList<>(mInventory.length); - // List order = new ArrayList<>(mInventory.length); - for (int i = 0; i < mInventory.length - 1; i++) { - if (!isValidSlot(i)) continue; - validSlots.add(i); - ItemStack s = mInventory[i]; - if (s == null) continue; - GTUtility.ItemId sID = GTUtility.ItemId.createNoCopy(s); - slots.merge(sID, s.stackSize, Integer::sum); - if (!stacks.containsKey(sID)) stacks.put(sID, s); - // order.add(sID); - mInventory[i] = null; - } - int i = 0; - for (Map.Entry entry : slots.entrySet()) { - do { - int slot = validSlots.get(i); - mInventory[slot] = stacks.get(entry.getKey()) - .copy(); - int toSet = Math.min(entry.getValue(), mInventory[slot].getMaxStackSize()); - mInventory[slot].stackSize = toSet; - entry.setValue(entry.getValue() - toSet); - i++; - } while (entry.getValue() > 0); - } - } - @Override protected void addMainUI(ModularWindow.Builder builder) { builder.widget( diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/MTEIndustrialApiary.java b/src/main/java/gregtech/common/tileentities/machines/basic/MTEIndustrialApiary.java index b1434ec7b3d..bf205a3499e 100644 --- a/src/main/java/gregtech/common/tileentities/machines/basic/MTEIndustrialApiary.java +++ b/src/main/java/gregtech/common/tileentities/machines/basic/MTEIndustrialApiary.java @@ -20,7 +20,6 @@ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TOP_INDUSTRIAL_APIARY_GLOW; import static gregtech.api.metatileentity.BaseTileEntity.STALLED_STUTTERING_TOOLTIP; import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; -import static gregtech.api.util.GTUtility.moveMultipleItemStacks; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -111,6 +110,7 @@ import gregtech.api.render.TextureFactory; import gregtech.api.util.GTApiaryModifier; import gregtech.api.util.GTApiaryUpgrade; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; import mcp.mobius.waila.api.IWailaConfigHandler; import mcp.mobius.waila.api.IWailaDataAccessor; @@ -639,24 +639,12 @@ && hasEnoughEnergyToCheckRecipe()) { aBaseMetaTileEntity.setActive(false); if (doesAutoOutput() && !isOutputEmpty() && aBaseMetaTileEntity.getFrontFacing() != mMainFacing) { - final TileEntity tTileEntity2 = aBaseMetaTileEntity - .getTileEntityAtSide(aBaseMetaTileEntity.getFrontFacing()); - final long tStoredEnergy = aBaseMetaTileEntity.getUniversalEnergyStored(); - int tMaxStacks = (int) (tStoredEnergy / 64L); - if (tMaxStacks > mOutputItems.length) tMaxStacks = mOutputItems.length; - - moveMultipleItemStacks( - aBaseMetaTileEntity, - tTileEntity2, - aBaseMetaTileEntity.getFrontFacing(), - aBaseMetaTileEntity.getBackFacing(), - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1, - tMaxStacks); + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.outOfMachine(this, aBaseMetaTileEntity.getFrontFacing()); + transfer.setStacksToTransfer(mOutputItems.length); + + transfer.transfer(); } if (aBaseMetaTileEntity.isAllowedToWork() && checkRecipe() == FOUND_AND_SUCCESSFULLY_USED_RECIPE) diff --git a/src/main/java/gregtech/common/tileentities/machines/basic/MTETeleporter.java b/src/main/java/gregtech/common/tileentities/machines/basic/MTETeleporter.java index c1d70151ef5..4b1a46817ca 100644 --- a/src/main/java/gregtech/common/tileentities/machines/basic/MTETeleporter.java +++ b/src/main/java/gregtech/common/tileentities/machines/basic/MTETeleporter.java @@ -31,7 +31,6 @@ import net.minecraft.entity.projectile.EntityFireball; import net.minecraft.entity.projectile.EntityFishHook; import net.minecraft.entity.projectile.EntityThrowable; -import net.minecraft.inventory.IInventory; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; @@ -60,6 +59,7 @@ import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.metatileentity.implementations.MTEBasicTank; import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTUtility; import gregtech.common.config.MachineStats; import ic2.core.block.EntityItnt; @@ -307,28 +307,19 @@ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { tTile = tWorld.getTileEntity(this.mTargetX, this.mTargetY, this.mTargetZ); } } - if (tTile instanceof IInventory) { - int tStacksize = mInventory[0].stackSize; - GTUtility.moveOneItemStack( - this, - tTile, - ForgeDirection.DOWN, - ForgeDirection.DOWN, - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - if (mInventory[0] == null || mInventory[0].stackSize < tStacksize) { - getBaseMetaTileEntity().decreaseStoredEnergyUnits( - (long) (Math.pow(tDistance, 1.5) * tDistance - * (tStacksize - (mInventory[0] == null ? 0 : mInventory[0].stackSize)) - * sFPowerMultiplyer), - false); - } - } + + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.source(this, ForgeDirection.UNKNOWN); + transfer.sink(tTile, ForgeDirection.UNKNOWN); + + int transferred = transfer.transfer(); + + getBaseMetaTileEntity().decreaseStoredEnergyUnits( + (long) (Math.pow(tDistance, 1.5) * tDistance * transferred * sFPowerMultiplyer), + false); } + List entities_in_box = getBaseMetaTileEntity().getWorld() .getEntitiesWithinAABB( Entity.class, diff --git a/src/main/java/gregtech/common/tileentities/storage/MTEDigitalChestBase.java b/src/main/java/gregtech/common/tileentities/storage/MTEDigitalChestBase.java index 30a80ff4853..7bbb77a507d 100644 --- a/src/main/java/gregtech/common/tileentities/storage/MTEDigitalChestBase.java +++ b/src/main/java/gregtech/common/tileentities/storage/MTEDigitalChestBase.java @@ -5,6 +5,7 @@ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_SCHEST_GLOW; import java.util.List; +import java.util.OptionalInt; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -17,6 +18,14 @@ import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.gtnewhorizon.gtnhlib.capability.item.AbstractInventorySourceIterator; +import com.gtnewhorizon.gtnhlib.capability.item.IItemIO; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSink; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSource; +import com.gtnewhorizon.gtnhlib.capability.item.InventorySourceIterator; import com.gtnewhorizons.modularui.api.NumberFormatMUI; import com.gtnewhorizons.modularui.api.screen.ModularWindow; import com.gtnewhorizons.modularui.api.screen.UIBuildContext; @@ -383,6 +392,21 @@ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex return GTUtility.areStacksEqual(getItemStack(), aStack); } + @Override + protected IItemSink getItemSink(ForgeDirection side) { + return new ItemIO(); + } + + @Override + protected IItemSource getItemSource(ForgeDirection side) { + return new ItemIO(); + } + + @Override + protected IItemIO getItemIO(ForgeDirection side) { + return new ItemIO(); + } + @Override public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing, int colorIndex, boolean aActive, boolean redstoneLevel) { @@ -458,4 +482,77 @@ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildCont value -> clientItemCount = value)); } + + class ItemIO implements IItemIO { + + @Override + public ItemStack store(ItemStack stack) { + ItemStack existing = getItemStack(); + + if (GTUtility.isStackValid(existing) && !GTUtility.areStacksEqual(existing, stack)) return stack; + + int capacity = getMaxItemCount(); + int toInsert = Math.min(capacity - getItemCount(), stack.stackSize); + + stack.stackSize -= toInsert; + + setItemStack(GTUtility.copyAmount(1, stack)); + setItemCount(getItemCount() + toInsert); + meInventoryHandler.notifyListeners(toInsert, stack); + + markDirty(); + + return stack.stackSize == 0 ? null : stack; + } + + @Override + public OptionalInt getStoredAmount(@Nullable ItemStack stack) { + ItemStack existing = getItemStack(); + + if (!GTUtility.areStacksEqual(existing, stack)) return ZERO; + + return OptionalInt.of(existing.stackSize); + } + + private static final int[] SLOTS = { 0 }; + + @Override + public @NotNull InventorySourceIterator iterator() { + return new AbstractInventorySourceIterator(SLOTS) { + + @Override + protected ItemStack getStackInSlot(int slot) { + if (slot != 0) return null; + + return GTUtility.copyAmountUnsafe(getItemCount(), getItemStack()); + } + + @Override + protected void setInventorySlotContents(int slot, @Nullable ItemStack stack) { + if (slot != 0) return; + + ItemStack current = getStackInSlot(0); + + setItemStack(GTUtility.copyAmount(1, stack)); + setItemCount(stack == null ? 0 : stack.stackSize); + + if (current != null && !GTUtility.areStacksEqual(stack, current)) { + meInventoryHandler.notifyListeners(-current.stackSize, current); + current = null; + } + + if (stack != null) { + int previouslyStored = current != null ? current.stackSize : 0; + + meInventoryHandler.notifyListeners(stack.stackSize - previouslyStored, stack); + } + } + + @Override + protected void markDirty() { + MTEDigitalChestBase.this.markDirty(); + } + }; + } + } } diff --git a/src/main/java/gregtech/loaders/preload/LoaderMetaTileEntities.java b/src/main/java/gregtech/loaders/preload/LoaderMetaTileEntities.java index 5c65aa605c9..e303f6ca16a 100644 --- a/src/main/java/gregtech/loaders/preload/LoaderMetaTileEntities.java +++ b/src/main/java/gregtech/loaders/preload/LoaderMetaTileEntities.java @@ -53,6 +53,7 @@ import gregtech.api.enums.ItemList; import gregtech.api.enums.MachineType; import gregtech.api.enums.SoundResource; +import gregtech.api.enums.VoltageIndex; import gregtech.api.metatileentity.implementations.MTEBasicBatteryBuffer; import gregtech.api.metatileentity.implementations.MTEBasicHull; import gregtech.api.metatileentity.implementations.MTEBasicMachineWithRecipe; @@ -62,6 +63,7 @@ import gregtech.api.metatileentity.implementations.MTEHatchEnergy; import gregtech.api.metatileentity.implementations.MTEHatchInput; import gregtech.api.metatileentity.implementations.MTEHatchInputBus; +import gregtech.api.metatileentity.implementations.MTEHatchInputBusCompressed; import gregtech.api.metatileentity.implementations.MTEHatchMagnet; import gregtech.api.metatileentity.implementations.MTEHatchMaintenance; import gregtech.api.metatileentity.implementations.MTEHatchMuffler; @@ -69,6 +71,7 @@ import gregtech.api.metatileentity.implementations.MTEHatchNanite; import gregtech.api.metatileentity.implementations.MTEHatchOutput; import gregtech.api.metatileentity.implementations.MTEHatchOutputBus; +import gregtech.api.metatileentity.implementations.MTEHatchOutputBusCompressed; import gregtech.api.metatileentity.implementations.MTEHatchQuadrupleHumongous; import gregtech.api.metatileentity.implementations.MTEHatchVoid; import gregtech.api.metatileentity.implementations.MTEHatchVoidBus; @@ -9180,6 +9183,136 @@ private static void registerVoidBus() { .set(new MTEHatchVoidBus(VOID_BUS.ID, "hatch.void_bus.tier.00", "Void Bus").getStackForm(1L)); } + private static void registerCompressedBus() { + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_COMPRESSED_1.ID, + "hatch.comp-output-bus.tier.00", + "Compressed Output Bus (LuV)", + VoltageIndex.LuV, + 16, + 256); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_COMPRESSED_2.ID, + "hatch.comp-output-bus.tier.01", + "Compressed Output Bus (ZPM)", + VoltageIndex.ZPM, + 16, + 2048); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_COMPRESSED_3.ID, + "hatch.comp-output-bus.tier.02", + "Compressed Output Bus (UV)", + VoltageIndex.UV, + 16, + 16384); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_COMPRESSED_4.ID, + "hatch.comp-output-bus.tier.03", + "Compressed Output Bus (UHV)", + VoltageIndex.UHV, + 16, + 131072); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_QUANTUM_1.ID, + "hatch.quantum-output-bus.tier.00", + "Quantum Output Bus (UEV)", + VoltageIndex.UEV, + 16, + 2097152); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_QUANTUM_2.ID, + "hatch.quantum-output-bus.tier.01", + "Quantum Output Bus (UIV)", + VoltageIndex.UIV, + 16, + Integer.MAX_VALUE / 64L); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_QUANTUM_3.ID, + "hatch.quantum-output-bus.tier.02", + "Quantum Output Bus (UMV)", + VoltageIndex.UMV, + 16, + Integer.MAX_VALUE * 64); + + new MTEHatchOutputBusCompressed( + HATCH_OUTPUT_BUS_QUANTUM_4.ID, + "hatch.quantum-output-bus.tier.03", + "Quantum Output Bus (UXV)", + VoltageIndex.UXV, + 16, + Long.MAX_VALUE / 64L); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_COMPRESSED_1.ID, + "hatch.comp-input-bus.tier.00", + "Compressed Input Bus (LuV)", + VoltageIndex.LuV, + 16, + 256); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_COMPRESSED_2.ID, + "hatch.comp-input-bus.tier.01", + "Compressed Input Bus (ZPM)", + VoltageIndex.ZPM, + 16, + 2048); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_COMPRESSED_3.ID, + "hatch.comp-input-bus.tier.02", + "Compressed Input Bus (UV)", + VoltageIndex.UV, + 16, + 16384); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_COMPRESSED_4.ID, + "hatch.comp-input-bus.tier.03", + "Compressed Input Bus (UHV)", + VoltageIndex.UHV, + 16, + 131072); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_QUANTUM_1.ID, + "hatch.quantum-input-bus.tier.00", + "Quantum Input Bus (UEV)", + VoltageIndex.UEV, + 16, + 2097152); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_QUANTUM_2.ID, + "hatch.quantum-input-bus.tier.01", + "Quantum Input Bus (UIV)", + VoltageIndex.UIV, + 16, + Integer.MAX_VALUE / 64L); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_QUANTUM_3.ID, + "hatch.quantum-input-bus.tier.02", + "Quantum Input Bus (UMV)", + VoltageIndex.UMV, + 16, + Integer.MAX_VALUE * 64); + + new MTEHatchInputBusCompressed( + HATCH_INPUT_BUS_QUANTUM_4.ID, + "hatch.quantum-input-bus.tier.03", + "Quantum Input Bus (UXV)", + VoltageIndex.UXV, + 16, + Long.MAX_VALUE / 64L); + } + private static void registerQuantumTank() { ItemList.Quantum_Tank_LV .set(new MTEQuantumTank(QUANTUM_TANK_LV.ID, "quantum.tank.tier.06", "Quantum Tank I", 6).getStackForm(1L)); @@ -10914,6 +11047,7 @@ public void run() { registerInputBus(); registerOutputBus(); registerVoidBus(); + registerCompressedBus(); registerMufflerHatch(); registerBoiler(); registerBatteryBuffer1x1(); diff --git a/src/main/java/gtPlusPlus/core/block/machine/BlockSuperJukebox.java b/src/main/java/gtPlusPlus/core/block/machine/BlockSuperJukebox.java index 2b230b14847..4b303703029 100644 --- a/src/main/java/gtPlusPlus/core/block/machine/BlockSuperJukebox.java +++ b/src/main/java/gtPlusPlus/core/block/machine/BlockSuperJukebox.java @@ -342,21 +342,13 @@ public boolean selectRecordToPlayFromInventoryAndSetViaVanillaHandler() { if (g != null && aSlotCounter <= 17) { Logger.INFO("b3 - " + g.getDisplayName()); if (GTUtility.areStacksEqual(g, aRecordToPlay, true)) { - IInventory aThisInv = this.getInventory(); - if (aThisInv.getStackInSlot(20) != null) { + IInventory inv = this.getInventory(); + if (inv.getStackInSlot(20) != null) { openDiscDrive(); } - GTUtility.moveStackFromSlotAToSlotB( - aThisInv, - aThisInv, - aSlotCounter, - 20, - (byte) 1, - (byte) 1, - (byte) 1, - (byte) 1); - setCurrentRecord(aThisInv.getStackInSlot(20)); + GTUtility.swapSlots(inv, 20, aSlotCounter); + setCurrentRecord(inv.getStackInSlot(20)); World aWorld = this.worldObj; int aX = this.xCoord; @@ -416,9 +408,9 @@ public boolean openDiscDrive() { g = this.getInventory() .getInventory()[i]; if (g == null) { - IInventory aThisInv = this.getInventory(); - GTUtility - .moveStackFromSlotAToSlotB(aThisInv, aThisInv, 20, i, (byte) 1, (byte) 1, (byte) 1, (byte) 1); + IInventory inv = this.getInventory(); + GTUtility.swapSlots(inv, 20, i); + vanillaStopJukebox(); Logger.INFO("b++"); this.markDirty(); diff --git a/src/main/java/gtPlusPlus/core/util/minecraft/ItemUtils.java b/src/main/java/gtPlusPlus/core/util/minecraft/ItemUtils.java index adbb625732b..bfa23891f6a 100644 --- a/src/main/java/gtPlusPlus/core/util/minecraft/ItemUtils.java +++ b/src/main/java/gtPlusPlus/core/util/minecraft/ItemUtils.java @@ -408,27 +408,7 @@ public static boolean checkForInvalidItems(ItemStack[] mInput) { } public static IInventory organiseInventory(IInventory aInputInventory) { - ItemStack[] p = new ItemStack[aInputInventory.getSizeInventory()]; - for (int o = 0; o < aInputInventory.getSizeInventory(); o++) { - p[o] = aInputInventory.getStackInSlot(o); - } - // ItemStack[] g = organiseInventory(p); - - for (int i = 0; i < p.length; ++i) { - for (int j = i + 1; j < p.length; ++j) { - if (p[j] != null && (p[i] == null || GTUtility.areStacksEqual(p[i], p[j]))) { - GTUtility.moveStackFromSlotAToSlotB( - aInputInventory, - aInputInventory, - j, - i, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } - } - } + GTUtility.compactStandardInventory(aInputInventory); return aInputInventory; } diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchInputBattery.java b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchInputBattery.java index 66b77324d96..8cc2b17ea01 100644 --- a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchInputBattery.java +++ b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchInputBattery.java @@ -149,19 +149,7 @@ public void updateSlots() { } protected void fillStacksIntoFirstSlots() { - for (int i = 0; i < mInventory.length; i++) - for (int j = i + 1; j < mInventory.length; j++) if (mInventory[j] != null - && (mInventory[i] == null || GTUtility.areStacksEqual(mInventory[i], mInventory[j]))) { - GTUtility.moveStackFromSlotAToSlotB( - getBaseMetaTileEntity(), - getBaseMetaTileEntity(), - j, - i, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } + GTUtility.compactInventory(this); } @Override diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchOutputBattery.java b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchOutputBattery.java index a3f2ca742c8..ea944f2d289 100644 --- a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchOutputBattery.java +++ b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTEHatchOutputBattery.java @@ -120,19 +120,7 @@ public void updateSlots() { } protected void fillStacksIntoFirstSlots() { - for (int i = 0; i < mInventory.length; i++) - for (int j = i + 1; j < mInventory.length; j++) if (mInventory[j] != null - && (mInventory[i] == null || GTUtility.areStacksEqual(mInventory[i], mInventory[j]))) { - GTUtility.moveStackFromSlotAToSlotB( - getBaseMetaTileEntity(), - getBaseMetaTileEntity(), - j, - i, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } + GTUtility.compactInventory(this); } @Override diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTESuperBusOutput.java b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTESuperBusOutput.java index 24403ad8ca6..7fbe2941ee8 100644 --- a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTESuperBusOutput.java +++ b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/MTESuperBusOutput.java @@ -56,22 +56,7 @@ public void updateSlots() { } protected void fillStacksIntoFirstSlots() { - for (int i = 0; i < this.mInventory.length; ++i) { - for (int j = i + 1; j < this.mInventory.length; ++j) { - if (this.mInventory[j] != null && (this.mInventory[i] == null - || GTUtility.areStacksEqual(this.mInventory[i], this.mInventory[j]))) { - GTUtility.moveStackFromSlotAToSlotB( - this.getBaseMetaTileEntity(), - this.getBaseMetaTileEntity(), - j, - i, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } - } - } + GTUtility.compactInventory(this); } @Override diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/nbthandlers/MTEHatchNbtConsumable.java b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/nbthandlers/MTEHatchNbtConsumable.java index e4952e4d11c..4efb569e7be 100644 --- a/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/nbthandlers/MTEHatchNbtConsumable.java +++ b/src/main/java/gtPlusPlus/xmod/gregtech/api/metatileentity/implementations/nbthandlers/MTEHatchNbtConsumable.java @@ -110,22 +110,7 @@ protected void validateUsageSlots() { // Only moves items in the first four slots protected final void fillStacksIntoFirstSlots() { - for (int i = 0; i <= getSlotID_LastInput(); i++) { - for (int j = i + 1; j <= getSlotID_LastInput(); j++) { - if (mInventory[j] != null - && (mInventory[i] == null || GTUtility.areStacksEqual(mInventory[i], mInventory[j]))) { - GTUtility.moveStackFromSlotAToSlotB( - getBaseMetaTileEntity(), - getBaseMetaTileEntity(), - j, - i, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1); - } - } - } + GTUtility.compactInventory(this, 0, getSlotID_LastInput() + 1); } public final void tryFillUsageSlots() { diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/automation/MTEElectricAutoWorkbench.java b/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/automation/MTEElectricAutoWorkbench.java index 4cb2d289f86..e084bc346c8 100644 --- a/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/automation/MTEElectricAutoWorkbench.java +++ b/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/automation/MTEElectricAutoWorkbench.java @@ -26,6 +26,7 @@ import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.implementations.MTEBasicTank; import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTItemTransfer; import gregtech.api.util.GTModHandler; import gregtech.api.util.GTOreDictUnificator; import gregtech.api.util.GTUtility; @@ -600,19 +601,11 @@ && getBaseMetaTileEntity().getUniversalEnergyStored() >= (mMode == 5 || mMode == } if (mThroughPut < 2) { - getBaseMetaTileEntity().decreaseStoredEnergyUnits( - GTUtility.moveOneItemStack( - getBaseMetaTileEntity(), - getBaseMetaTileEntity().getIInventoryAtSide(getBaseMetaTileEntity().getBackFacing()), - getBaseMetaTileEntity().getBackFacing(), - getBaseMetaTileEntity().getFrontFacing(), - null, - false, - (byte) 64, - (byte) 1, - (byte) 64, - (byte) 1) * 10, - true); + GTItemTransfer transfer = new GTItemTransfer(); + + transfer.outOfMachine(this, aBaseMetaTileEntity.getBackFacing()); + + getBaseMetaTileEntity().decreaseStoredEnergyUnits(transfer.transfer() * 10L, true); } } } diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/storage/creative/MTEInfiniteItemHolder.java b/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/storage/creative/MTEInfiniteItemHolder.java index 91492e123f4..71938e6d25d 100644 --- a/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/storage/creative/MTEInfiniteItemHolder.java +++ b/src/main/java/gtPlusPlus/xmod/gregtech/common/tileentities/storage/creative/MTEInfiniteItemHolder.java @@ -1,9 +1,20 @@ package gtPlusPlus.xmod.gregtech.common.tileentities.storage.creative; +import java.util.OptionalInt; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.gtnewhorizon.gtnhlib.capability.item.AbstractInventorySourceIterator; +import com.gtnewhorizon.gtnhlib.capability.item.IItemIO; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSink; +import com.gtnewhorizon.gtnhlib.capability.item.IItemSource; +import com.gtnewhorizon.gtnhlib.capability.item.InventorySourceIterator; + import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.metatileentity.MetaTileEntity; @@ -88,4 +99,50 @@ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { return new MTEInfiniteItemHolder(this.mName, this.mTier, this.mDescriptionArray, this.mTextures); } + + @Override + protected IItemSink getItemSink(ForgeDirection side) { + return new ItemIO(); + } + + @Override + protected IItemSource getItemSource(ForgeDirection side) { + return new ItemIO(); + } + + @Override + protected IItemIO getItemIO(ForgeDirection side) { + return new ItemIO(); + } + + class ItemIO implements IItemIO { + + @Override + public ItemStack store(ItemStack stack) { + return null; + } + + @Override + public OptionalInt getStoredAmount(@Nullable ItemStack stack) { + return GTUtility.areStacksEqual(stack, mItemStack) ? OptionalInt.of(Integer.MAX_VALUE) : ZERO; + } + + @Override + public @NotNull InventorySourceIterator iterator() { + return new AbstractInventorySourceIterator(new int[] { 0 }) { + + @Override + protected ItemStack getStackInSlot(int slot) { + if (slot != 0) return null; + + return GTUtility.copyAmountUnsafe(Integer.MAX_VALUE, mItemStack); + } + + @Override + protected void setInventorySlotContents(int slot, ItemStack stack) { + // do nothing + } + }; + } + } } diff --git a/src/main/resources/assets/gregtech/lang/en_US.lang b/src/main/resources/assets/gregtech/lang/en_US.lang index 81f9357419e..0b2ff1b50dd 100644 --- a/src/main/resources/assets/gregtech/lang/en_US.lang +++ b/src/main/resources/assets/gregtech/lang/en_US.lang @@ -1410,6 +1410,7 @@ GT5U.gui.button.power_panel=Open Power Panel GT5U.gui.button.turbinemenu=Open Chamber Controller GT5U.gui.button.machineinfo=Display Machine Information GT5U.gui.button.max_parallel=Always use maximum +GT5U.gui.button.compressed_bus_settings=Open Bus Settings GT5U.gui.text.rangedvalue=Range: %s-%s GT5U.gui.text.lockedvalue=Locked: %s GT5U.gui.text.success=§aProcessing recipe @@ -1544,6 +1545,9 @@ GT5U.gui.text.alpha-transfer=Alpha Transfer GT5U.gui.text.beta-minus=Beta Minus Decay GT5U.gui.text.beta-plus=Beta Plus Decay GT5U.gui.text.invalidfluidsup=Invalid Fluid Supplied +GT5U.gui.text.stack_capacity=Stack Capacity +GT5U.gui.text.bus_settings=Bus Settings +GT5U.gui.text.amount_out_of=§9Amount: %,d / %,d GT5U.gui.config.client=Client GT5U.gui.config.client.color_modulation=Color Modulation