package gregtech.common.tileentities.machines; import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_HATCH; import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ME_HATCH_ACTIVE; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import javax.annotation.Nullable; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.common.util.ForgeDirection; import com.gtnewhorizons.modularui.api.screen.ModularWindow; import com.gtnewhorizons.modularui.api.screen.UIBuildContext; import appeng.api.AEApi; import appeng.api.implementations.IPowerChannelState; import appeng.api.networking.GridFlags; 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.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.api.util.AECableType; import appeng.items.storage.ItemBasicStorageCell; 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.GTMod; import gregtech.api.enums.ItemList; import gregtech.api.gui.modularui.GTUIInfos; import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.implementations.MTEHatchOutputBus; import gregtech.api.render.TextureFactory; import gregtech.api.util.GTUtility; public class MTEHatchOutputBusME extends MTEHatchOutputBus implements IPowerChannelState { private long baseCapacity = 1_600; private BaseActionSource requestSource = null; private @Nullable AENetworkProxy gridProxy = null; final IItemList itemCache = AEApi.instance() .storage() .createItemList(); long lastOutputTick = 0; long lastInputTick = 0; long tickCounter = 0; boolean additionalConnection = false; public MTEHatchOutputBusME(int aID, String aName, String aNameRegional) { super( aID, aName, aNameRegional, 3, new String[] { "Item Output for Multiblocks", "Stores directly into ME", "Can cache 1600 items by default", "Change cache size by inserting a storage cell", "Change ME connection behavior by right-clicking with wire cutter" }, 1); } public MTEHatchOutputBusME(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { super(aName, aTier, 1, aDescription, aTextures); } @Override public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { return new MTEHatchOutputBusME(mName, mTier, mDescriptionArray, mTextures); } @Override public ITexture[] getTexturesActive(ITexture aBaseTexture) { return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_HATCH_ACTIVE) }; } @Override public ITexture[] getTexturesInactive(ITexture aBaseTexture) { return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_ME_HATCH) }; } @Override public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { super.onFirstTick(aBaseMetaTileEntity); getProxy().onReady(); } @Override public boolean storeAll(ItemStack aStack) { aStack.stackSize = store(aStack); return aStack.stackSize == 0; } private long getCachedAmount() { long itemAmount = 0; for (IAEItemStack item : itemCache) { itemAmount += item.getStackSize(); } return itemAmount; } private long getCacheCapacity() { ItemStack upgradeItemStack = mInventory[0]; if (upgradeItemStack != null && upgradeItemStack.getItem() instanceof ItemBasicStorageCell) { return ((ItemBasicStorageCell) upgradeItemStack.getItem()).getBytesLong(upgradeItemStack) * 8; } return baseCapacity; } /** * Check if the internal cache can still fit more items in it */ public boolean canAcceptItem() { if (getCachedAmount() < getCacheCapacity()) { return true; } return false; } /** * Attempt to store items in connected ME network. Returns how many items did not fit (if the network was down e.g.) * * @param stack input stack * @return amount of items left over */ public int store(final ItemStack stack) { // Always allow insertion on the same tick so we can output the entire recipe if (canAcceptItem() || (lastInputTick == tickCounter)) { itemCache.add( AEApi.instance() .storage() .createItemStack(stack)); lastInputTick = tickCounter; return 0; } return stack.stackSize; } private BaseActionSource getRequest() { if (requestSource == null) requestSource = new MachineSource((IActionHost) getBaseMetaTileEntity()); return requestSource; } @Override public AECableType getCableConnectionType(ForgeDirection forgeDirection) { return isOutputFacing(forgeDirection) ? AECableType.SMART : AECableType.NONE; } private void updateValidGridProxySides() { if (additionalConnection) { getProxy().setValidSides(EnumSet.complementOf(EnumSet.of(ForgeDirection.UNKNOWN))); } else { getProxy().setValidSides(EnumSet.of(getBaseMetaTileEntity().getFrontFacing())); } } @Override public void onFacingChange() { updateValidGridProxySides(); } @Override public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { GTUIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); return true; } @Override public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { if (!getBaseMetaTileEntity().getCoverInfoAtSide(side) .isGUIClickable()) return; } @Override public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, float aX, float aY, float aZ) { additionalConnection = !additionalConnection; updateValidGridProxySides(); aPlayer.addChatComponentMessage( new ChatComponentTranslation("GT5U.hatch.additionalConnection." + additionalConnection)); return true; } @Override public AENetworkProxy getProxy() { if (gridProxy == null) { if (getBaseMetaTileEntity() instanceof IGridProxyable) { gridProxy = new AENetworkProxy( (IGridProxyable) getBaseMetaTileEntity(), "proxy", ItemList.Hatch_Output_Bus_ME.get(1), true); gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL); updateValidGridProxySides(); if (getBaseMetaTileEntity().getWorld() != null) gridProxy.setOwner( getBaseMetaTileEntity().getWorld() .getPlayerEntityByName(getBaseMetaTileEntity().getOwnerName())); } } return this.gridProxy; } private void flushCachedStack() { AENetworkProxy proxy = getProxy(); if (proxy == null) { return; } try { IMEMonitor sg = proxy.getStorage() .getItemInventory(); for (IAEItemStack s : itemCache) { if (s.getStackSize() == 0) continue; IAEItemStack rest = Platform.poweredInsert(proxy.getEnergy(), sg, s, getRequest()); if (rest != null && rest.getStackSize() > 0) { s.setStackSize(rest.getStackSize()); break; } s.setStackSize(0); } } catch (final GridAccessException ignored) { } lastOutputTick = tickCounter; } @Override public boolean isPowered() { return getProxy() != null && getProxy().isPowered(); } @Override public boolean isActive() { return getProxy() != null && getProxy().isActive(); } @Override public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { if (getBaseMetaTileEntity().isServerSide()) { tickCounter = aTick; if (tickCounter > (lastOutputTick + 40)) flushCachedStack(); if (tickCounter % 20 == 0) getBaseMetaTileEntity().setActive(isActive()); } super.onPostTick(aBaseMetaTileEntity, aTick); } @Override public void addAdditionalTooltipInformation(ItemStack stack, List tooltip) { if (stack.hasTagCompound() && stack.stackTagCompound.hasKey("baseCapacity")) { tooltip.add( "Current cache capacity: " + EnumChatFormatting.YELLOW + ReadableNumberConverter.INSTANCE .toWideReadableForm(stack.stackTagCompound.getLong("baseCapacity"))); } } @Override public void setItemNBT(NBTTagCompound aNBT) { super.setItemNBT(aNBT); aNBT.setLong("baseCapacity", baseCapacity); } @Override public void saveNBTData(NBTTagCompound aNBT) { super.saveNBTData(aNBT); NBTTagList items = new NBTTagList(); for (IAEItemStack s : itemCache) { if (s.getStackSize() == 0) continue; NBTTagCompound tag = new NBTTagCompound(); tag.setTag("itemStack", GTUtility.saveItem(s.getItemStack())); tag.setLong("size", s.getStackSize()); items.appendTag(tag); } aNBT.setBoolean("additionalConnection", additionalConnection); aNBT.setTag("cachedItems", items); aNBT.setLong("baseCapacity", baseCapacity); getProxy().writeToNBT(aNBT); } @Override public void loadNBTData(NBTTagCompound aNBT) { super.loadNBTData(aNBT); NBTBase t = aNBT.getTag("cachedStack"); // legacy if (t instanceof NBTTagCompound) itemCache.add( AEApi.instance() .storage() .createItemStack(GTUtility.loadItem((NBTTagCompound) t))); t = aNBT.getTag("cachedItems"); if (t instanceof NBTTagList l) { for (int i = 0; i < l.tagCount(); ++i) { NBTTagCompound tag = l.getCompoundTagAt(i); if (!tag.hasKey("itemStack")) { // legacy #868 itemCache.add( AEApi.instance() .storage() .createItemStack(GTUtility.loadItem(l.getCompoundTagAt(i)))); continue; } NBTTagCompound tagItemStack = tag.getCompoundTag("itemStack"); final IAEItemStack s = AEApi.instance() .storage() .createItemStack(GTUtility.loadItem(tagItemStack)); if (s != null) { s.setStackSize(tag.getLong("size")); itemCache.add(s); } else { GTMod.GT_FML_LOGGER.warn( "An error occurred while loading contents of ME Output Bus. This item has been voided: " + tagItemStack); } } } additionalConnection = aNBT.getBoolean("additionalConnection"); baseCapacity = aNBT.getLong("baseCapacity"); // Set the base capacity of existing hatches to be infinite if (baseCapacity == 0) { baseCapacity = Long.MAX_VALUE; } getProxy().readFromNBT(aNBT); } @Override public boolean isGivingInformation() { return true; } @Override public String[] getInfoData() { List ss = new ArrayList<>(); ss.add( "The bus is " + ((getProxy() != null && getProxy().isActive()) ? EnumChatFormatting.GREEN + "online" : EnumChatFormatting.RED + "offline" + getAEDiagnostics()) + EnumChatFormatting.RESET); IWideReadableNumberConverter nc = ReadableNumberConverter.INSTANCE; ss.add("Item cache capacity: " + nc.toWideReadableForm(getCacheCapacity())); if (itemCache.isEmpty()) { ss.add("The bus has no cached items"); } else { ss.add(String.format("The bus contains %d cached stacks: ", itemCache.size())); int counter = 0; for (IAEItemStack s : itemCache) { ss.add( s.getItem() .getItemStackDisplayName(s.getItemStack()) + ": " + EnumChatFormatting.GOLD + nc.toWideReadableForm(s.getStackSize()) + EnumChatFormatting.RESET); if (++counter > 100) break; } } return ss.toArray(new String[itemCache.size() + 2]); } @Override public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { getBaseMetaTileEntity().add1by1Slot(builder); } @Override public boolean acceptsItemLock() { return false; } }