aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/common
diff options
context:
space:
mode:
authorHarry <harryyunull@gmail.com>2023-07-24 03:55:26 -0400
committerGitHub <noreply@github.com>2023-07-24 09:55:26 +0200
commit75633e41387b6ef37dc02b7e0cc5076936c9b3ed (patch)
tree11ecfb9b62b8d9815aeabba5f06fe372cdd7a02b /src/main/java/gregtech/common
parent4a4881d79f40b8b800f9aa858542195efd1849d0 (diff)
downloadGT5-Unofficial-75633e41387b6ef37dc02b7e0cc5076936c9b3ed.tar.gz
GT5-Unofficial-75633e41387b6ef37dc02b7e0cc5076936c9b3ed.tar.bz2
GT5-Unofficial-75633e41387b6ef37dc02b7e0cc5076936c9b3ed.zip
Add Crafting Input Buffer (ME) (#2160)
* Crafting input buffer * persist data * rename and stuff * no more waiting for multis to start * rearrange ui * spotless * fixes * refactor * Crafting Input Slave * getCrafterIcon * crafting input bus * spotless * Update GT_MetaTileEntity_MultiBlockBase.java * Update GT_Loader_MetaTileEntities.java * Update GT_Loader_MetaTileEntities.java * Update GT_MetaTileEntity_MultiBlockBase.java * Update GT_MetaTileEntity_MultiBlockBase.java * Update GT_Loader_MetaTileEntities.java --------- Co-authored-by: Martin Robertz <dream-master@gmx.net>
Diffstat (limited to 'src/main/java/gregtech/common')
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java701
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java180
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java16
-rw-r--r--src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java11
4 files changed, 908 insertions, 0 deletions
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java
new file mode 100644
index 0000000000..ee7a3df356
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_ME.java
@@ -0,0 +1,701 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_HATCH;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_HATCH_ACTIVE;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.InventoryCrafting;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.jetbrains.annotations.NotNull;
+
+import com.glodblock.github.common.item.ItemFluidPacket;
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import appeng.api.AEApi;
+import appeng.api.implementations.ICraftingPatternItem;
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.crafting.ICraftingPatternDetails;
+import appeng.api.networking.crafting.ICraftingProvider;
+import appeng.api.networking.crafting.ICraftingProviderHelper;
+import appeng.api.networking.events.MENetworkCraftingPatternChange;
+import appeng.api.networking.security.BaseActionSource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.MachineSource;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.data.IAEFluidStack;
+import appeng.api.storage.data.IAEItemStack;
+import appeng.api.util.AECableType;
+import appeng.api.util.DimensionalCoord;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.util.IWideReadableNumberConverter;
+import appeng.util.Platform;
+import appeng.util.ReadableNumberConverter;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.render.TextureFactory;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaTileEntity_Hatch_CraftingInput_ME extends GT_MetaTileEntity_Hatch_InputBus
+ implements IConfigurationCircuitSupport, IAddGregtechLogo, IAddUIWidgets, IPowerChannelState, ICraftingProvider,
+ IGridProxyable, IDualInputHatch {
+
+ // Each pattern slot in the crafting input hatch has its own internal inventory
+ public static class PatternSlot implements IDualInputInventory {
+
+ public interface SharedItemGetter {
+
+ ItemStack[] getSharedItem();
+ }
+
+ private ItemStack pattern;
+ private ICraftingPatternDetails patternDetails;
+ private List<ItemStack> itemInventory;
+ private List<FluidStack> fluidInventory;
+ private SharedItemGetter sharedItemGetter;
+
+ public PatternSlot(ItemStack pattern, World world, SharedItemGetter getter) {
+ this.pattern = pattern;
+ this.patternDetails = ((ICraftingPatternItem) Objects.requireNonNull(pattern.getItem()))
+ .getPatternForItem(pattern, world);
+ this.itemInventory = new ArrayList<>();
+ this.fluidInventory = new ArrayList<>();
+ this.sharedItemGetter = getter;
+ }
+
+ public PatternSlot(NBTTagCompound nbt, World world, SharedItemGetter getter) {
+ this.pattern = ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("pattern"));
+ this.patternDetails = ((ICraftingPatternItem) Objects.requireNonNull(pattern.getItem()))
+ .getPatternForItem(pattern, world);
+ this.itemInventory = new ArrayList<>();
+ this.fluidInventory = new ArrayList<>();
+ this.sharedItemGetter = getter;
+ NBTTagList inv = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < inv.tagCount(); i++) {
+ itemInventory.add(ItemStack.loadItemStackFromNBT(inv.getCompoundTagAt(i)));
+ }
+ NBTTagList fluidInv = nbt.getTagList("fluidInventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < fluidInv.tagCount(); i++) {
+ fluidInventory.add(FluidStack.loadFluidStackFromNBT(fluidInv.getCompoundTagAt(i)));
+ }
+ }
+
+ public boolean hasChanged(ItemStack newPattern, World world) {
+ return newPattern == null
+ || (!ItemStack.areItemStacksEqual(pattern, newPattern) && !this.patternDetails.equals(
+ ((ICraftingPatternItem) Objects.requireNonNull(pattern.getItem()))
+ .getPatternForItem(pattern, world)));
+ }
+
+ public ItemStack[] getItemInputs() {
+ return ArrayUtils.addAll(itemInventory.toArray(new ItemStack[0]), sharedItemGetter.getSharedItem());
+ }
+
+ public FluidStack[] getFluidInputs() {
+ return fluidInventory.toArray(new FluidStack[0]);
+ }
+
+ public ICraftingPatternDetails getPatternDetails() {
+ return patternDetails;
+ }
+
+ public void refund(AENetworkProxy proxy, BaseActionSource src) throws GridAccessException {
+ IMEMonitor<IAEItemStack> sg = proxy.getStorage()
+ .getItemInventory();
+ for (ItemStack itemStack : itemInventory) {
+ if (itemStack == null || itemStack.stackSize == 0) continue;
+ IAEItemStack rest = Platform.poweredInsert(
+ proxy.getEnergy(),
+ sg,
+ AEApi.instance()
+ .storage()
+ .createItemStack(itemStack),
+ src);
+ itemStack.stackSize = rest != null && rest.getStackSize() > 0 ? (int) rest.getStackSize() : 0;
+ }
+ IMEMonitor<IAEFluidStack> fsg = proxy.getStorage()
+ .getFluidInventory();
+ for (FluidStack fluidStack : fluidInventory) {
+ if (fluidStack == null || fluidStack.amount == 0) continue;
+ IAEFluidStack rest = Platform.poweredInsert(
+ proxy.getEnergy(),
+ fsg,
+ AEApi.instance()
+ .storage()
+ .createFluidStack(fluidStack),
+ src);
+ fluidStack.amount = rest != null && rest.getStackSize() > 0 ? (int) rest.getStackSize() : 0;
+ }
+ }
+
+ public void insertItemsAndFluids(InventoryCrafting inventoryCrafting) {
+ for (int i = 0; i < inventoryCrafting.getSizeInventory(); ++i) {
+ ItemStack itemStack = inventoryCrafting.getStackInSlot(i);
+ if (itemStack == null) continue;
+
+ boolean inserted = false;
+ if (itemStack.getItem() instanceof ItemFluidPacket) { // insert fluid
+ var fluidStack = ItemFluidPacket.getFluidStack(itemStack);
+ if (fluidStack == null) continue;
+ for (var fluid : fluidInventory) {
+ if (fluid.isFluidEqual(fluidStack)) {
+ fluid.amount += fluidStack.amount;
+ inserted = true;
+ break;
+ }
+ }
+ if (!inserted) {
+ fluidInventory.add(fluidStack);
+ }
+ } else { // insert item
+ for (var item : itemInventory) {
+ if (itemStack.isItemEqual(item)) {
+ item.stackSize += itemStack.stackSize;
+ inserted = true;
+ break;
+ }
+ }
+ if (!inserted) {
+ itemInventory.add(itemStack);
+ }
+ }
+ }
+ }
+
+ public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
+ nbt.setTag("pattern", pattern.writeToNBT(new NBTTagCompound()));
+
+ NBTTagList itemInventoryNbt = new NBTTagList();
+ for (ItemStack itemStack : this.itemInventory) {
+ itemInventoryNbt.appendTag(itemStack.writeToNBT(new NBTTagCompound()));
+ }
+ nbt.setTag("inventory", itemInventoryNbt);
+
+ NBTTagList fluidInventoryNbt = new NBTTagList();
+ for (FluidStack fluidStack : fluidInventory) {
+ fluidInventoryNbt.appendTag(fluidStack.writeToNBT(new NBTTagCompound()));
+ }
+ nbt.setTag("fluidInventory", fluidInventoryNbt);
+
+ return nbt;
+ }
+ }
+
+ // mInventory is used for storing patterns, circuit and manual slot (typically NC items)
+ private static final int MAX_PATTERN_COUNT = 4 * 8;
+ private static final int MAX_INV_COUNT = MAX_PATTERN_COUNT + 2;
+ private static final int SLOT_MANUAL = MAX_INV_COUNT - 1;
+ private static final int SLOT_CIRCUIT = MAX_INV_COUNT - 2;
+
+ private BaseActionSource requestSource = null;
+ private @Nullable AENetworkProxy gridProxy = null;
+
+ // holds all internal inventories
+ private PatternSlot[] internalInventory = new PatternSlot[MAX_PATTERN_COUNT];
+
+ // a hash map for faster lookup of pattern slots, not necessarily all valid.
+ private Map<ICraftingPatternDetails, PatternSlot> patternDetailsPatternSlotMap = new HashMap<>(MAX_PATTERN_COUNT);
+
+ private boolean initialPatternSyncDone = false;
+ private boolean justHadNewItems = false;
+
+ private boolean supportFluids;
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME(int aID, String aName, String aNameRegional,
+ boolean supportFluids) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ 1,
+ MAX_INV_COUNT,
+ new String[] { "Advanced item input for Multiblocks", "Processes patterns directly from ME",
+ supportFluids ? "It supports patterns including fluids"
+ : "It does not support patterns including fluids" });
+ disableSort = true;
+ this.supportFluids = supportFluids;
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures, boolean supportFluids) {
+ super(aName, aTier, MAX_INV_COUNT, aDescription, aTextures);
+ this.supportFluids = supportFluids;
+ disableSort = true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_CraftingInput_ME(mName, mTier, mDescriptionArray, mTextures, supportFluids);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_HATCH_ACTIVE) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_HATCH) };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+
+ if (!initialPatternSyncDone && aTimer % 10 == 0 && getBaseMetaTileEntity().isServerSide()) {
+ try {
+ getProxy().getGrid()
+ .postEvent(new MENetworkCraftingPatternChange(this, getProxy().getNode()));
+ } catch (GridAccessException ignored) {
+ return;
+ }
+ initialPatternSyncDone = true;
+ }
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ getProxy().onReady();
+ }
+
+ @Override
+ public IGridNode getGridNode(ForgeDirection dir) {
+ return getProxy().getNode();
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return isOutputFacing(forgeDirection) ? AECableType.SMART : AECableType.NONE;
+ }
+
+ @Override
+ public void securityBreak() {}
+
+ @Override
+ public AENetworkProxy getProxy() {
+ if (gridProxy == null) {
+ gridProxy = new AENetworkProxy(this, "proxy", ItemList.Hatch_CraftingInput_Bus_ME.get(1), true);
+ gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+ if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner(
+ getBaseMetaTileEntity().getWorld()
+ .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName()));
+ }
+
+ return this.gridProxy;
+ }
+
+ @Override
+ public DimensionalCoord getLocation() {
+ return new DimensionalCoord(
+ getBaseMetaTileEntity().getWorld(),
+ getBaseMetaTileEntity().getXCoord(),
+ getBaseMetaTileEntity().getYCoord(),
+ getBaseMetaTileEntity().getZCoord());
+ }
+
+ @Override
+ public void gridChanged() {
+ super.gridChanged();
+ if (getProxy().isReady()) {
+ getProxy().getNode()
+ .updateState();
+ }
+ }
+
+ @Override
+ public boolean isPowered() {
+ return getProxy() != null && getProxy().isPowered();
+ }
+
+ @Override
+ public boolean isActive() {
+ return getProxy() != null && getProxy().isActive();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+
+ // save internalInventory
+ NBTTagList internalInventoryNBT = new NBTTagList();
+ for (int i = 0; i < internalInventory.length; i++) {
+ if (internalInventory[i] != null) {
+ NBTTagCompound internalInventorySlotNBT = new NBTTagCompound();
+ internalInventorySlotNBT.setInteger("patternSlot", i);
+ internalInventorySlotNBT
+ .setTag("patternSlotNBT", internalInventory[i].writeToNBT(new NBTTagCompound()));
+ internalInventoryNBT.appendTag(internalInventorySlotNBT);
+ }
+ }
+ aNBT.setTag("internalInventory", internalInventoryNBT);
+
+ if (GregTech_API.mAE2) {
+ getProxy().writeToNBT(aNBT);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ // load internalInventory
+ NBTTagList internalInventoryNBT = aNBT.getTagList("internalInventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < internalInventoryNBT.tagCount(); i++) {
+ NBTTagCompound internalInventorySlotNBT = internalInventoryNBT.getCompoundTagAt(i);
+ int patternSlot = internalInventorySlotNBT.getInteger("patternSlot");
+ internalInventory[patternSlot] = new PatternSlot(
+ internalInventorySlotNBT.getCompoundTag("patternSlotNBT"),
+ getBaseMetaTileEntity().getWorld(),
+ this::getSharedItems);
+ }
+
+ // reconstruct patternDetailsPatternSlotMap
+ patternDetailsPatternSlotMap.clear();
+ for (PatternSlot patternSlot : internalInventory) {
+ if (patternSlot != null) {
+ patternDetailsPatternSlotMap.put(patternSlot.getPatternDetails(), patternSlot);
+ }
+ }
+
+ if (GregTech_API.mAE2) {
+ getProxy().readFromNBT(aNBT);
+ }
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ private String describePattern(ICraftingPatternDetails patternDetails) {
+ return Arrays.stream(patternDetails.getCondensedOutputs())
+ .map(
+ aeItemStack -> aeItemStack.getItem()
+ .getItemStackDisplayName(aeItemStack.getItemStack()))
+ .collect(Collectors.joining(", "));
+ }
+
+ @Override
+ public String[] getInfoData() {
+ if (GregTech_API.mAE2) {
+ var ret = new ArrayList<String>();
+ ret.add(
+ "The bus is " + ((getProxy() != null && getProxy().isActive()) ? EnumChatFormatting.GREEN + "online"
+ : EnumChatFormatting.RED + "offline" + getAEDiagnostics()) + EnumChatFormatting.RESET);
+ ret.add("Internal Inventory: ");
+ var i = 0;
+ for (var slot : internalInventory) {
+ if (slot == null) continue;
+ IWideReadableNumberConverter nc = ReadableNumberConverter.INSTANCE;
+
+ i += 1;
+ ret.add(
+ "Slot " + i
+ + " "
+ + EnumChatFormatting.BLUE
+ + describePattern(slot.patternDetails)
+ + EnumChatFormatting.RESET);
+ for (var item : slot.itemInventory) {
+ if (item == null || item.stackSize == 0) continue;
+ ret.add(
+ item.getItem()
+ .getItemStackDisplayName(item) + ": "
+ + EnumChatFormatting.GOLD
+ + nc.toWideReadableForm(item.stackSize)
+ + EnumChatFormatting.RESET);
+ }
+ for (var fluid : slot.fluidInventory) {
+ if (fluid == null || fluid.amount == 0) continue;
+ ret.add(
+ fluid.getLocalizedName() + ": "
+ + EnumChatFormatting.AQUA
+ + nc.toWideReadableForm(fluid.amount)
+ + EnumChatFormatting.RESET);
+ }
+ }
+ return ret.toArray(new String[0]);
+ } else return new String[] {};
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return SLOT_CIRCUIT;
+ }
+
+ @Override
+ public int getCircuitSlotX() {
+ return 152;
+ }
+
+ @Override
+ public int getCircuitSlotY() {
+ return 64;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.@NotNull Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 8)
+ .startFromSlot(0)
+ .endAtSlot(MAX_PATTERN_COUNT - 1)
+ .phantom(false)
+ .background(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_PATTERN_ME)
+ .widgetCreator(
+ slot -> new SlotWidget(slot)
+ .setFilter(itemStack -> itemStack.getItem() instanceof ICraftingPatternItem)
+ .setChangeListener(() -> onPatternChange(slot)))
+ .build()
+ .setPos(7, 9))
+ .widget(
+ new SlotWidget(inventoryHandler, SLOT_MANUAL).setShiftClickPriority(11)
+ .setBackground(getGUITextureSet().getItemSlot())
+ .setPos(151, 45))
+ .widget(new ButtonWidget().setOnClick((clickData, widget) -> {
+ if (clickData.mouseButton == 0) {
+ refundAll();
+ }
+ })
+ .setPlayClickSound(true)
+ .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_EXPORT)
+ .addTooltips(ImmutableList.of("Return all internally stored items back to AE"))
+ .setSize(16, 16)
+ .setPos(152, 28));
+ }
+
+ @Override
+ public void updateSlots() {
+ if (mInventory[SLOT_MANUAL] != null && mInventory[SLOT_MANUAL].stackSize <= 0) mInventory[SLOT_MANUAL] = null;
+ }
+
+ private BaseActionSource getRequest() {
+ if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity());
+ return requestSource;
+ }
+
+ private void onPatternChange(BaseSlot slot) {
+ if (!getBaseMetaTileEntity().isServerSide()) return;
+
+ var world = getBaseMetaTileEntity().getWorld();
+
+ // remove old if applicable
+ var originalPattern = internalInventory[slot.getSlotIndex()];
+ if (originalPattern != null) {
+ if (originalPattern.hasChanged(slot.getStack(), world)) {
+ try {
+ originalPattern.refund(getProxy(), getRequest());
+ } catch (GridAccessException ignored) {}
+ internalInventory[slot.getSlotIndex()] = null;
+ } else {
+ return; // nothing has changed
+ }
+ }
+
+ // original does not exist or has changed
+ var pattern = slot.getStack();
+ if (pattern == null || !(pattern.getItem() instanceof ICraftingPatternItem)) return;
+
+ var patternSlot = new PatternSlot(pattern, world, this::getSharedItems);
+ internalInventory[slot.getSlotIndex()] = patternSlot;
+ patternDetailsPatternSlotMap.put(patternSlot.getPatternDetails(), patternSlot);
+
+ try {
+ getProxy().getGrid()
+ .postEvent(new MENetworkCraftingPatternChange(this, getProxy().getNode()));
+ } catch (GridAccessException ignored) {}
+ }
+
+ private ItemStack[] getSharedItems() {
+ return new ItemStack[] { mInventory[SLOT_CIRCUIT], mInventory[SLOT_MANUAL] };
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+ if (tag.hasKey("inventory")) {
+ var inventory = tag.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < inventory.tagCount(); ++i) {
+ var item = inventory.getCompoundTagAt(i);
+ var name = item.getString("name");
+ var amount = item.getInteger("amount");
+ currenttip.add(
+ name + ": "
+ + EnumChatFormatting.GOLD
+ + ReadableNumberConverter.INSTANCE.toWideReadableForm(amount)
+ + EnumChatFormatting.RESET);
+ }
+ }
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+
+ NBTTagList inventory = new NBTTagList();
+ HashMap<String, Integer> nameToAmount = new HashMap<>();
+ for (Iterator<PatternSlot> it = inventories(); it.hasNext();) {
+ var i = it.next();
+ for (var item : i.itemInventory) {
+ if (item != null && item.stackSize > 0) {
+ var name = item.getDisplayName();
+ var amount = nameToAmount.getOrDefault(name, 0);
+ nameToAmount.put(name, amount + item.stackSize);
+ }
+ }
+ for (var fluid : i.fluidInventory) {
+ if (fluid != null && fluid.amount > 0) {
+ var name = fluid.getLocalizedName();
+ var amount = nameToAmount.getOrDefault(name, 0);
+ nameToAmount.put(name, amount + fluid.amount);
+ }
+ }
+ }
+ for (var entry : nameToAmount.entrySet()) {
+ var item = new NBTTagCompound();
+ item.setString("name", entry.getKey());
+ item.setInteger("amount", entry.getValue());
+ inventory.appendTag(item);
+ }
+
+ tag.setTag("inventory", inventory);
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+
+ @Override
+ public void provideCrafting(ICraftingProviderHelper craftingTracker) {
+ if (!isActive()) return;
+
+ for (PatternSlot slot : internalInventory) {
+ if (slot == null) continue;
+ craftingTracker.addCraftingOption(this, slot.getPatternDetails());
+ }
+ }
+
+ @Override
+ public boolean pushPattern(ICraftingPatternDetails patternDetails, InventoryCrafting table) {
+ if (!isActive()) return false;
+
+ if (!supportFluids) {
+ for (int i = 0; i < table.getSizeInventory(); ++i) {
+ ItemStack itemStack = table.getStackInSlot(i);
+ if (itemStack == null) continue;
+ if (itemStack.getItem() instanceof ItemFluidPacket) return false;
+ }
+ }
+
+ patternDetailsPatternSlotMap.get(patternDetails)
+ .insertItemsAndFluids(table);
+ justHadNewItems = true;
+ return true;
+ }
+
+ @Override
+ public boolean isBusy() {
+ return false;
+ }
+
+ public Iterator<PatternSlot> inventories() {
+ return Arrays.stream(internalInventory)
+ .filter(Objects::nonNull)
+ .iterator();
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ refundAll();
+ super.onBlockDestroyed();
+ }
+
+ private void refundAll() {
+ for (var slot : internalInventory) {
+ if (slot == null) continue;
+ try {
+ slot.refund(getProxy(), getRequest());
+ } catch (GridAccessException ignored) {}
+ }
+ }
+
+ public boolean justUpdated() {
+ var ret = justHadNewItems;
+ justHadNewItems = false;
+ return ret;
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return;
+
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, true, true)) return;
+
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setString("type", "CraftingInputBuffer");
+ tag.setInteger("x", aBaseMetaTileEntity.getXCoord());
+ tag.setInteger("y", aBaseMetaTileEntity.getYCoord());
+ tag.setInteger("z", aBaseMetaTileEntity.getZCoord());
+
+ dataStick.stackTagCompound = tag;
+ dataStick.setStackDisplayName(
+ "Crafting Input Buffer Link Data Stick (" + aBaseMetaTileEntity
+ .getXCoord() + ", " + aBaseMetaTileEntity.getYCoord() + ", " + aBaseMetaTileEntity.getZCoord() + ")");
+ aPlayer.addChatMessage(new ChatComponentText("Saved Link Data to Data Stick"));
+ }
+
+ @Override
+ public ItemStack getCrafterIcon() {
+ return getMachineCraftingIcon();
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java
new file mode 100644
index 0000000000..04bf9da06d
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/GT_MetaTileEntity_Hatch_CraftingInput_Slave.java
@@ -0,0 +1,180 @@
+package gregtech.common.tileentities.machines;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_HATCH;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_INPUT_HATCH_ACTIVE;
+
+import java.util.*;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ChatComponentText;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_Hatch_CraftingInput_Slave extends GT_MetaTileEntity_Hatch_InputBus
+ implements IDualInputHatch {
+
+ private GT_MetaTileEntity_Hatch_CraftingInput_ME master; // use getMaster() to access
+ private int masterX, masterY, masterZ;
+ private boolean masterSet = false; // indicate if values of masterX, masterY, masterZ are valid
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_Slave(int aID, String aName, String aNameRegional) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ 1,
+ 0,
+ new String[] { "Slave for Crafting Input Buffer",
+ "Link with Crafting Input Buffer using Data Stick to share inventory",
+ "Left click on the Crafting Input Buffer, then right click on this block to link them", });
+ disableSort = true;
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_Slave(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ disableSort = true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_CraftingInput_Slave(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_HATCH_ACTIVE) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_INPUT_HATCH) };
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ super.onPostTick(aBaseMetaTileEntity, aTimer);
+ if (aTimer % 100 == 0 && masterSet && getMaster() == null) {
+ trySetMasterFromCoord(masterX, masterY, masterZ);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+
+ if (aNBT.hasKey("master")) {
+ NBTTagCompound masterNBT = aNBT.getCompoundTag("master");
+ masterX = masterNBT.getInteger("x");
+ masterY = masterNBT.getInteger("y");
+ masterZ = masterNBT.getInteger("z");
+ masterSet = true;
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (masterSet) {
+ NBTTagCompound masterNBT = new NBTTagCompound();
+ masterNBT.setInteger("x", masterX);
+ masterNBT.setInteger("y", masterY);
+ masterNBT.setInteger("z", masterZ);
+ aNBT.setTag("master", masterNBT);
+ }
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ var ret = new ArrayList<String>();
+ if (getMaster() != null) {
+ ret.add(
+ "This bus is linked to the Crafting Input Buffer at " + masterX
+ + ", "
+ + masterY
+ + ", "
+ + masterZ
+ + ".");
+ ret.addAll(Arrays.asList(getMaster().getInfoData()));
+ } else ret.add("This bus is not linked to any Crafting Input Buffer.");
+ return ret.toArray(new String[0]);
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME getMaster() {
+ if (master == null) return null;
+ if (master.getBaseMetaTileEntity() == null) { // master disappeared
+ master = null;
+ }
+ return master;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ public Iterator<GT_MetaTileEntity_Hatch_CraftingInput_ME.PatternSlot> inventories() {
+ return getMaster() != null ? getMaster().inventories() : Collections.emptyIterator();
+ }
+
+ public boolean justUpdated() {
+ return getMaster() != null && getMaster().justUpdated();
+ }
+
+ public GT_MetaTileEntity_Hatch_CraftingInput_ME trySetMasterFromCoord(int x, int y, int z) {
+ var tileEntity = getBaseMetaTileEntity().getWorld()
+ .getTileEntity(x, y, z);
+ if (tileEntity == null) return null;
+ if (!(tileEntity instanceof IGregTechTileEntity gtTileEntity)) return null;
+ var metaTileEntity = gtTileEntity.getMetaTileEntity();
+ if (!(metaTileEntity instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME)) return null;
+ masterX = x;
+ masterY = y;
+ masterZ = z;
+ masterSet = true;
+ master = (GT_MetaTileEntity_Hatch_CraftingInput_ME) metaTileEntity;
+ return master;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return super.onRightclick(aBaseMetaTileEntity, aPlayer);
+ ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, true, true))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer);
+ if (!dataStick.hasTagCompound() || !"CraftingInputBuffer".equals(dataStick.stackTagCompound.getString("type")))
+ return false;
+
+ NBTTagCompound nbt = dataStick.stackTagCompound;
+ int x = nbt.getInteger("x");
+ int y = nbt.getInteger("y");
+ int z = nbt.getInteger("z");
+ if (trySetMasterFromCoord(x, y, z) != null) {
+ aPlayer.addChatMessage(new ChatComponentText("Link successful"));
+ return true;
+ }
+ aPlayer.addChatMessage(new ChatComponentText("Link failed"));
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java b/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
new file mode 100644
index 0000000000..54b1acbdfa
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
@@ -0,0 +1,16 @@
+package gregtech.common.tileentities.machines;
+
+import java.util.Iterator;
+
+import net.minecraft.item.ItemStack;
+
+public interface IDualInputHatch {
+
+ boolean justUpdated();
+
+ Iterator<? extends IDualInputInventory> inventories();
+
+ void updateTexture(int id);
+
+ void updateCraftingIcon(ItemStack icon);
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java b/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
new file mode 100644
index 0000000000..01649fe181
--- /dev/null
+++ b/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
@@ -0,0 +1,11 @@
+package gregtech.common.tileentities.machines;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+public interface IDualInputInventory {
+
+ ItemStack[] getItemInputs();
+
+ FluidStack[] getFluidInputs();
+}