/* This file is part of Project-Zed. Project-Zed is free software: you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Project-Zed is * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along * with Project-Zed. If not, see <http://www.gnu.org/licenses/> */ package com.projectzed.mod.tileentity.container.pipe; import com.hockeyhurd.hcorelib.api.math.Vector3; import com.hockeyhurd.hcorelib.api.math.VectorHelper; import com.hockeyhurd.hcorelib.api.util.BlockUtils; import com.projectzed.api.energy.source.EnumColor; import com.projectzed.api.energy.source.IColorComponent; import com.projectzed.api.fluid.FluidNetwork; import com.projectzed.api.fluid.FluidNode; import com.projectzed.api.fluid.IFluidTile; import com.projectzed.api.fluid.container.IFluidContainer; import com.projectzed.api.tileentity.IModularFrame; import com.projectzed.api.tileentity.container.AbstractTileEntityPipe; import com.projectzed.mod.handler.PacketHandler; import com.projectzed.mod.handler.message.MessageTileEntityLiquiduct; import com.projectzed.mod.util.Reference; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fluids.*; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.ArrayList; import java.util.List; /** * Class containing te code for liquiducts. * <br><bold>NOTE: </bold>Although this class isn't abstract, it should be treated like such. * * @author hockeyhurd * @version Feb 12, 2015 */ public class TileEntityLiquiductBase extends AbstractTileEntityPipe implements IFluidContainer, IFluidTile, IColorComponent { // protected int maxFluidStorage = 2000; protected int maxFluidStorage = 0; // temp set '0' protected int importRate, exportRate; protected FluidTank internalTank; protected FluidNetwork network; protected boolean isMaster; protected FluidStack lastStackTransfer; protected boolean transferredLastTick; protected boolean hadNetworkNBT; protected Vector3<Integer> masterVec; /** * @param name */ public TileEntityLiquiductBase(String name) { super(name); internalTank = new FluidTank(this.maxFluidStorage); this.importRate = Reference.Constants.BASE_FLUID_TRANSFER_RATE; this.exportRate = Reference.Constants.BASE_FLUID_TRANSFER_RATE; } /** * Function to get the tank associated with this fluid pipe. * * @return tank object. */ public FluidTank getTank() { return internalTank; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#getLocalizedFluidName() */ @Override public String getLocalizedFluidName() { if (getTank() == null || getTank().getFluid() == null) return "<empty fluid>"; String ret = getTank().getFluid().getLocalizedName(); return ret != null && ret.length() > 0 ? ret : "<empty fluid>"; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#getFluidID() */ @Override public String getFluidID() { return getTank().getFluid() != null ? getTank().getFluid().getFluid().getName() : null; } /* * (non-Javadoc) * @see com.projectzed.api.energy.source.IColorComponent#getColor() */ @Override public EnumColor getColor() { return null; } /* * (non-Javadoc) * @see com.projectzed.api.energy.source.IColorComponent#setColor(com.projectzed.api.energy.source.EnumColor) */ @Override public void setColor(EnumColor color) { } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#getMaxFluidImportRate() */ @Override public int getMaxFluidImportRate() { return this.importRate; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#getMaxFluidExportRate() */ @Override public int getMaxFluidExportRate() { return this.exportRate; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#isPipe() */ @Override public boolean isPipe() { return true; } protected void importContents() { if (this.worldObj == null || this.worldObj.isRemote) return; if (this.internalTank.getFluidAmount() > this.maxFluidStorage) { FluidStack copy = this.internalTank.getFluid(); copy.amount = this.maxFluidStorage; this.internalTank.setFluid(copy); return; } // FluidNet.importFluidFromNeighbors(this, worldObj, xCoord, yCoord, zCoord, lastReceivedDir); // FluidNet.tryClearDirectionalTraffic(this, worldObj, xCoord, yCoord, zCoord, lastReceivedDir); } protected void exportContents() { if (this.worldObj == null || this.worldObj.isRemote) return; if (this.internalTank.getFluidAmount() == 0) return; // FluidNet.exportFluidToNeighbors(this, worldObj, xCoord, yCoord, zCoord); } /* * (non-Javadoc) * @see com.projectzed.api.tileentity.container.AbstractTileEntityPipe#updateEntity() */ @Override public void update() { super.update(); updateNetwork(); // importContents(); // exportContents(); if (!this.worldObj.isRemote && this.worldObj.getTotalWorldTime() % 20L == 0) { // if (this.lastReceivedDir != EnumFacing.UNKNOWN) ProjectZed.logHelper.info(this.lastReceivedDir.name()); PacketHandler.INSTANCE.sendToAll(new MessageTileEntityLiquiduct(this)); } // if (!this.getWorldObj().isRemote) System.out.println(getTank().getFluidAmount()); // if (!this.getWorldObj().isRemote && getTank().getFluidAmount() > 0) ProjectZed.logHelper.info(getTank().getFluidAmount(), lastReceivedDir.name(), worldVec().toString()); } /* (non-Javadoc) * @see com.projectzed.api.tileentity.container.AbstractTileEntityEnergyContainer#getDescriptionPacket() */ /*@Override public Packet getDescriptionPacket() { return PacketHandler.INSTANCE.getPacketFrom(new MessageTileEntityLiquiduct(this)); }*/ @Override public NBTTagCompound getUpdateTag() { PacketHandler.INSTANCE.getPacketFrom(new MessageTileEntityLiquiduct(this)); final NBTTagCompound comp = getTileData(); saveNBT(comp); return comp; } /* * (non-Javadoc) * @see com.projectzed.api.tileentity.IWrenchable#worldVec() */ @Override public Vector3<Integer> worldVec() { return VectorHelper.toVector3i(pos); } /* * (non-Javadoc) * @see com.projectzed.api.tileentity.container.AbstractTileEntityPipe#updateConnections() */ @Override protected void updateConnections() { for (EnumFacing dir : EnumFacing.VALUES) { BlockPos blockPos = VectorHelper.toBlockPos(pos.getX() + dir.getFrontOffsetX(), pos.getY() + dir.getFrontOffsetY(), pos.getZ() + dir.getFrontOffsetZ()); TileEntity tileEntity = worldObj.getTileEntity(blockPos); if (tileEntity instanceof IFluidHandler) { IFluidHandler cont = (IFluidHandler) tileEntity; if (cont instanceof TileEntityLiquiductBase) if (((TileEntityLiquiductBase) cont).getColor() == getColor()) connections[dir.ordinal()] = dir.getOpposite(); else if (cont instanceof IModularFrame) if (((IModularFrame) cont).getSideValve(dir) != 0) connections[dir.ordinal()] = dir.getOpposite(); else connections[dir.ordinal()] = dir.getOpposite(); } else connections[dir.ordinal()] = null; } } @Override public double distanceTo(Vector3<Integer> vec) { return this.worldVec().getNetDifference(vec); } @Override public Vector3<Integer> getOffsetVec(int x, int y, int z) { final Vector3<Integer> ret = worldVec(); ret.x += x; ret.y += y; ret.z += z; return ret; } @Override public float getCost() { return 1.0f; } @Override public IBlockState getTile(World world) { return world != null ? BlockUtils.getBlock(world, getPos()) : null; } @Override public boolean isSolid() { return false; } /* * (non-Javadoc) * @see com.projectzed.api.tileentity.AbstractTileEntityGeneric#readFromNBT(net.minecraft.nbt.NBTTagCompound) */ @Override public void readFromNBT(NBTTagCompound comp) { super.readFromNBT(comp); this.internalTank.readFromNBT(comp); isMaster = comp.getBoolean("FluidNetworkIsMaster"); hadNetworkNBT = comp.getBoolean("FluidNetworkHadNetwork"); if (masterVec == null) masterVec = Vector3.zero.getVector3i(); masterVec.x = comp.getInteger("FluidNetworkMasterVector3X"); masterVec.y = comp.getInteger("FluidNetworkMasterVector3Y"); masterVec.z = comp.getInteger("FluidNetworkMasterVector3Z"); } /* * (non-Javadoc) * @see com.projectzed.api.tileentity.AbstractTileEntityGeneric#writeToNBT(net.minecraft.nbt.NBTTagCompound) */ @Override public NBTTagCompound writeToNBT(NBTTagCompound comp) { super.writeToNBT(comp); this.internalTank.writeToNBT(comp); // if (hasFluidNetwork()) network.getNodeAt(worldVec()).writeToNBT(comp); hadNetworkNBT = hasFluidNetwork(); comp.setBoolean("FluidNetworkIsMaster", isMaster); comp.setBoolean("FluidNetworkHadNetwork", hadNetworkNBT); if (hasFluidNetwork()) { this.masterVec = network.getMasterNode().worldVec().copy(); comp.setInteger("FluidNetworkMasterVector3X", this.masterVec.x); comp.setInteger("FluidNetworkMasterVector3Y", this.masterVec.y); comp.setInteger("FluidNetworkMasterVector3Z", this.masterVec.z); } return comp; } /* * (non-Javadoc) * @see net.minecraftforge.fluids.IFluidHandler#fill(net.minecraftforge.common.util.EnumFacing, net.minecraftforge.fluids.FluidStack, boolean) */ @Override public int fill(EnumFacing from, FluidStack resource, boolean doFill) { if (!worldObj.isRemote) { FluidStack altStack = resource.copy(); altStack.amount = this.getMaxFluidImportRate(); boolean useAlt = resource.amount > altStack.amount; int fillAmount = 0; if (useAlt) fillAmount = internalTank.fill(altStack, doFill); else fillAmount = internalTank.fill(resource, doFill); if (doFill) { // worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); worldObj.notifyBlockOfStateChange(pos, blockType); this.markDirty(); if (this.getBlockType() != null) worldObj.notifyNeighborsOfStateChange(pos, blockType); if (useAlt) FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(altStack, worldObj, pos, this.internalTank, fillAmount)); else FluidEvent.fireEvent(new FluidEvent.FluidFillingEvent(resource, worldObj, pos, this.internalTank, fillAmount)); } return fillAmount; } return 0; } /* * (non-Javadoc) * @see net.minecraftforge.fluids.IFluidHandler#drain(net.minecraftforge.common.util.EnumFacing, net.minecraftforge.fluids.FluidStack, boolean) */ @Override public FluidStack drain(EnumFacing from, FluidStack resource, boolean doDrain) { return drain(from, resource, -1, doDrain); } /* * (non-Javadoc) * @see net.minecraftforge.fluids.IFluidHandler#drain(net.minecraftforge.common.util.EnumFacing, int, boolean) */ @Override public FluidStack drain(EnumFacing from, int maxDrain, boolean doDrain) { return drain(from, null, maxDrain, doDrain); } /** * Drains fluid from this block to another. * * @param from = direction drained from. * @param drainFluid = the fluid drained. * @param drainAmount = amount of fluid drained. * @param doDrain = whether draining should be simulated or not. * @return type and amount of fluid drained. */ protected FluidStack drain(EnumFacing from, FluidStack drainFluid, int drainAmount, boolean doDrain) { if (!worldObj.isRemote) { FluidStack drainedFluid = (drainFluid != null && drainFluid.isFluidEqual(internalTank.getFluid())) ? internalTank.drain( drainFluid.amount, doDrain) : drainAmount >= 0 ? internalTank.drain(drainAmount, doDrain) : null; FluidStack altStack = drainedFluid.copy(); altStack.amount = this.getMaxFluidExportRate(); boolean useAlt = drainAmount > altStack.amount; if (doDrain && drainedFluid != null && drainedFluid.amount > 0) { this.markDirty(); worldObj.notifyBlockOfStateChange(pos, blockType); worldObj.notifyNeighborsOfStateChange(pos, blockType); if (useAlt) FluidEvent.fireEvent(new FluidEvent.FluidDrainingEvent(altStack, worldObj, pos, this.internalTank, altStack.amount)); else FluidEvent.fireEvent(new FluidEvent.FluidDrainingEvent(drainedFluid, worldObj, pos, this.internalTank, altStack.amount)); } } return null; } /* * (non-Javadoc) * @see net.minecraftforge.fluids.IFluidHandler#canFill(net.minecraftforge.common.util.EnumFacing, net.minecraftforge.fluids.Fluid) */ @Override public boolean canFill(EnumFacing from, Fluid fluid) { if (fluid != null && !isFull()) { FluidStack tankFluid = this.internalTank.getFluid(); return tankFluid == null || tankFluid.isFluidEqual(new FluidStack(fluid, 0)); } return false; } /* * (non-Javadoc) * @see net.minecraftforge.fluids.IFluidHandler#canDrain(net.minecraftforge.common.util.EnumFacing, net.minecraftforge.fluids.Fluid) */ @Override public boolean canDrain(EnumFacing from, Fluid fluid) { if (fluid != null && this.internalTank.getFluidAmount() > 0) { FluidStack tankFluid = this.internalTank.getFluid(); return tankFluid != null && tankFluid.isFluidEqual(new FluidStack(fluid, 0)); } return false; } /* * (non-Javadoc) * @see net.minecraftforge.fluids.IFluidHandler#getTankInfo(net.minecraftforge.common.util.EnumFacing) */ @Override public FluidTankInfo[] getTankInfo(EnumFacing from) { return new FluidTankInfo[] { this.internalTank.getInfo() }; } /** * Gets whether tank is full or not. * * @return true if full, else returns false. */ public boolean isFull() { return this.internalTank.getFluidAmount() == this.internalTank.getCapacity(); } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#canBeSourceNode() */ @Override public boolean canBeSourceNode() { return false; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#canBeMaster() */ @Override public boolean canBeMaster() { return true; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#isMaster() */ @Override public boolean isMaster() { return isMaster; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#setMaster(boolean) */ @Override public void setMaster(boolean master) { this.isMaster = master; } public boolean wasTransferredLastTick() { return transferredLastTick; } public FluidStack getTransferredStack() { return lastStackTransfer; } @SideOnly(Side.CLIENT) public void setLastTransferredStack(FluidStack stack) { this.lastStackTransfer = stack; } @SideOnly(Side.CLIENT) public void setWasTransferredLastTick(boolean value) { this.transferredLastTick = value; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#hasFluidNetwork() */ @Override public boolean hasFluidNetwork() { return network != null; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#getNetwork() */ @Override public FluidNetwork getNetwork() { return network; } /* * (non-Javadoc) * @see com.projectzed.api.fluid.container.IFluidContainer#setFluidNetwork(com.projectzed.api.fluid.FluidNetwork) */ @Override public void setFluidNetwork(FluidNetwork network) { this.network = network; this.hadNetworkNBT = network == null; } /** * Method used to void all fluid nodes in the network if this is the master. */ public void voidNetwork() { if (network != null && !network.isEmpty() && isMaster) { for (FluidNode node : network.getNodes()) { if (node.getFluidContainer() instanceof IFluidContainer) { IFluidContainer cont = (IFluidContainer) node.getIFluidContainer(); cont.setFluidNetwork(null); } } } } /** * Main update method used to add adjacent containers/tanks to fluid network and creating/joining fluid network(s). */ protected void updateNetwork() { if (worldObj.isRemote) return; // run every half second! if (worldObj.getTotalWorldTime() % (20L / 2) != 0) return; // only run if okayed to if (hadNetworkNBT) { Vector3<Integer> worldVec = worldVec(); // ProjectZed.logHelper.info("isMaster:", isMaster); // ProjectZed.logHelper.info("hadNetworkNBT", hadNetworkNBT); if (hasFluidNetwork()) { // ProjectZed.logHelper.info("Network connection established!"); hadNetworkNBT = false; } else { // get surrounding fluid handlers so that the network can track this node's connections! List<EnumFacing> directions = new ArrayList<EnumFacing>(EnumFacing.VALUES.length); for (EnumFacing dir : EnumFacing.VALUES) { BlockPos pos = VectorHelper.toBlockPos(worldVec.x + dir.getFrontOffsetX(), worldVec.y + dir.getFrontOffsetY(), worldVec.z + dir.getFrontOffsetZ()); TileEntity te = worldObj.getTileEntity(pos); if (te instanceof TileEntityLiquiductBase) { TileEntityLiquiductBase cont = (TileEntityLiquiductBase) te; if (cont.getColor() == getColor() && cont.hasFluidNetwork()) directions.add(dir); } else if (te instanceof IFluidHandler) directions.add(dir); } if (isMaster) { // create new fluid network. if (!hasFluidNetwork()) { directions.add(null); network = new FluidNetwork(this.worldObj, new FluidNode(this, directions.toArray(new EnumFacing[directions.size()]), worldVec())); } hadNetworkNBT = false; } // not master, contact TileEntity and obtain/add to fluid network this node. else { // ProjectZed.logHelper.info("Still waiting for hadNetworkNBT, returning.."); if (this.masterVec == null) return; IFluidContainer master = (IFluidContainer) worldObj.getTileEntity(VectorHelper.toBlockPos(masterVec)); if (master != null && master.hasFluidNetwork()) { this.network = master.getNetwork(); this.network.add(new FluidNode(this, directions.toArray(new EnumFacing[directions.size()]), worldVec())); hadNetworkNBT = false; } } return; } } if (!hasFluidNetwork()) { List<EnumFacing> directions = new ArrayList<EnumFacing>(EnumFacing.VALUES.length); Vector3<Integer> worldVec = worldVec(); // ProjectZed.logHelper.info("Help me find a network!"); for (EnumFacing dir : EnumFacing.VALUES) { BlockPos pos = VectorHelper.toBlockPos(worldVec.x + dir.getFrontOffsetX(), worldVec.y + dir.getFrontOffsetY(), worldVec.z + dir.getFrontOffsetZ()); TileEntity te = worldObj.getTileEntity(pos); if (te instanceof TileEntityLiquiductBase) { TileEntityLiquiductBase cont = (TileEntityLiquiductBase) te; if (cont.getColor() == getColor() && cont.hasFluidNetwork()) { network = cont.getNetwork(); directions.add(dir); // ProjectZed.logHelper.info("Yeah! I found a network and decided to join the fun!", cont.getNetwork().size()); } } } // create new fluid network. if (!hasFluidNetwork()) { directions.add(null); network = new FluidNetwork(this.worldObj, new FluidNode(this, directions.toArray(new EnumFacing[directions.size()]), worldVec())); // ProjectZed.logHelper.info("Network still not found, guess I'll start my own!"); } // if has a fluid network, add this node to the current network. else network.add(new FluidNode(this, directions.toArray(new EnumFacing[directions.size()]), worldVec())); } // if this node is apart of the fluid network check for other nodes to add and update if is master node. else { Vector3<Integer> worldVec = worldVec(); // merging networks together if applicable. for (EnumFacing dir : EnumFacing.VALUES) { BlockPos pos = VectorHelper.toBlockPos(worldVec.x + dir.getFrontOffsetX(), worldVec.y + dir.getFrontOffsetY(), worldVec.z + dir.getFrontOffsetZ()); TileEntity te = worldObj.getTileEntity(pos); if (te == null) continue; if (te instanceof IFluidHandler) { if (te instanceof TileEntityLiquiductBase) { TileEntityLiquiductBase te2 = (TileEntityLiquiductBase) te; if (te2.hasFluidNetwork() && !te2.network.equals(network)) { TileEntityLiquiductBase master = (TileEntityLiquiductBase) te2.getNetwork().getMasterNode().getFluidContainer(); // merge smaller (or equal to), other network with this network. if (te2.getNetwork().size() <= network.size()) { network.merge(master.getNetwork()); master.voidNetwork(); } // merge this smaller network with larger, other network. else if (te2.getNetwork().size() > network.size()) { master.getNetwork().merge(network); if (isMaster()) voidNetwork(); } } continue; } if (!hasFluidNetwork()) continue; Vector3<Integer> vec = VectorHelper.toVector3i(te.getPos()); List<EnumFacing> directions = new ArrayList<EnumFacing>(EnumFacing.VALUES.length); FluidNode n = network.getNodeAt(vec); if (n != null) { if (n.getConnections().length >= 1 && n.getConnections()[0] != null) { for (EnumFacing con : n.getConnections()) { directions.add(con); } } } directions.add(dir); network.add(new FluidNode((IFluidHandler) te, directions.toArray(new EnumFacing[directions.size()]), vec, FluidNode.appropriateValveType((IFluidHandler) te, dir))); } } // if this was the first node placed, then we must call update here! if (network != null && !network.isEmpty()) { // if not master, check if we are now the 'master'. /*if (!this.isMaster) */this.isMaster = network.isMasterNode(network.getNodeAt(worldVec())); if (this.isMaster) network.update(); if (network.getTransferringState()) { this.transferredLastTick = true; this.lastStackTransfer = network.getTransferredFluid().copy(); } else { this.transferredLastTick = false; this.lastStackTransfer = null; } } } } }