buildcraft.factory.TilePump.java Source code

Java tutorial

Introduction

Here is the source code for buildcraft.factory.TilePump.java

Source

/**
 * Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team
 * http://www.mod-buildcraft.com
 *
 * BuildCraft is distributed under the terms of the Minecraft Mod Public
 * License 1.0, or MMPL. Please check the contents of the license located in
 * http://www.mod-buildcraft.com/MMPL-1.0.txt
 */
package buildcraft.factory;

import java.io.IOException;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeMap;

import io.netty.buffer.ByteBuf;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;

import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;

import buildcraft.BuildCraftCore;
import buildcraft.BuildCraftFactory;
import buildcraft.api.core.SafeTimeTracker;
import buildcraft.api.gates.IAction;
import buildcraft.api.mj.MjBattery;
import buildcraft.core.BlockIndex;
import buildcraft.core.CoreConstants;
import buildcraft.core.EntityBlock;
import buildcraft.core.IMachine;
import buildcraft.core.TileBuffer;
import buildcraft.core.TileBuildCraft;
import buildcraft.core.fluids.FluidUtils;
import buildcraft.core.fluids.SingleUseTank;
import buildcraft.core.network.PacketPayload;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.BlockUtil;
import buildcraft.core.utils.Utils;

public class TilePump extends TileBuildCraft implements IMachine, IFluidHandler {

    public static final int REBUID_DELAY = 512;
    public static int MAX_LIQUID = FluidContainerRegistry.BUCKET_VOLUME * 16;
    public SingleUseTank tank = new SingleUseTank("tank", MAX_LIQUID, this);

    private EntityBlock tube;
    private TreeMap<Integer, Deque<BlockIndex>> pumpLayerQueues = new TreeMap<Integer, Deque<BlockIndex>>();
    private double tubeY = Double.NaN;
    private int aimY = 0;

    private TileBuffer[] tileBuffer = null;
    private SafeTimeTracker timer = new SafeTimeTracker(REBUID_DELAY);
    private int tick = Utils.RANDOM.nextInt();
    private int numFluidBlocksFound = 0;
    private boolean powered = false;

    @MjBattery(maxCapacity = 100, maxReceivedPerCycle = 15, minimumConsumption = 1)
    private double mjStored = 0;

    @Override
    public void updateEntity() {
        super.updateEntity();

        if (powered) {
            pumpLayerQueues.clear();
            destroyTube();
        } else {
            createTube();
        }

        if (worldObj.isRemote) {
            return;
        }

        pushToConsumers();

        if (powered) {
            return;
        }

        if (tube == null) {
            return;
        }

        if (tube.posY - aimY > 0.01) {
            tubeY = tube.posY - 0.01;
            setTubePosition();
            sendNetworkUpdate();
            return;
        }

        tick++;

        if (tick % 16 != 0) {
            return;
        }

        BlockIndex index = getNextIndexToPump(false);

        FluidStack fluidToPump = index != null ? BlockUtil.drainBlock(worldObj, index.x, index.y, index.z, false)
                : null;
        if (fluidToPump != null) {
            if (isFluidAllowed(fluidToPump.getFluid()) && tank.fill(fluidToPump, false) == fluidToPump.amount) {
                if (mjStored > 10) {
                    mjStored -= 10;

                    if (fluidToPump.getFluid() != FluidRegistry.WATER || BuildCraftCore.consumeWaterSources
                            || numFluidBlocksFound < 9) {
                        index = getNextIndexToPump(true);
                        BlockUtil.drainBlock(worldObj, index.x, index.y, index.z, true);
                    }

                    tank.fill(fluidToPump, true);
                }
            }
        } else {
            if (tick % 128 == 0) {
                // TODO: improve that decision
                rebuildQueue();

                if (getNextIndexToPump(false) == null) {
                    for (int y = yCoord - 1; y > 0; --y) {
                        if (isPumpableFluid(xCoord, y, zCoord)) {
                            aimY = y;
                            return;
                        } else if (isBlocked(xCoord, y, zCoord)) {
                            return;
                        }
                    }
                }
            }
        }
    }

    public void onNeighborBlockChange(Block block) {
        boolean p = worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord);

        if (powered != p) {
            powered = p;

            if (!worldObj.isRemote) {
                sendNetworkUpdate();
            }
        }
    }

    private boolean isBlocked(int x, int y, int z) {
        Material mat = worldObj.getBlock(x, y, z).getMaterial();

        return mat.blocksMovement();
    }

    private void pushToConsumers() {
        if (tileBuffer == null) {
            tileBuffer = TileBuffer.makeBuffer(worldObj, xCoord, yCoord, zCoord, false);
        }

        FluidUtils.pushFluidToConsumers(tank, 400, tileBuffer);
    }

    private TileEntity getTile(ForgeDirection side) {
        if (tileBuffer == null) {
            tileBuffer = TileBuffer.makeBuffer(worldObj, xCoord, yCoord, zCoord, false);
        }

        return tileBuffer[side.ordinal()].getTile();
    }

    private void createTube() {
        if (tube == null) {
            tube = FactoryProxy.proxy.newPumpTube(worldObj);

            if (!Double.isNaN(tubeY)) {
                tube.posY = tubeY;
            } else {
                tube.posY = yCoord;
            }

            tubeY = tube.posY;

            if (aimY == 0) {
                aimY = yCoord;
            }

            setTubePosition();

            worldObj.spawnEntityInWorld(tube);

            if (!worldObj.isRemote) {
                sendNetworkUpdate();
            }
        }
    }

    private void destroyTube() {
        if (tube != null) {
            CoreProxy.proxy.removeEntity(tube);
            tube = null;
            tubeY = Double.NaN;
            aimY = 0;
        }
    }

    private BlockIndex getNextIndexToPump(boolean remove) {
        if (pumpLayerQueues.isEmpty()) {
            if (timer.markTimeIfDelay(worldObj)) {
                rebuildQueue();
            }

            return null;
        }

        Deque<BlockIndex> topLayer = pumpLayerQueues.lastEntry().getValue();

        if (topLayer != null) {
            if (topLayer.isEmpty()) {
                pumpLayerQueues.pollLastEntry();
            }

            if (remove) {
                BlockIndex index = topLayer.pollLast();
                return index;
            } else {
                return topLayer.peekLast();
            }
        } else {
            return null;
        }
    }

    private Deque<BlockIndex> getLayerQueue(int layer) {
        Deque<BlockIndex> pumpQueue = pumpLayerQueues.get(layer);

        if (pumpQueue == null) {
            pumpQueue = new LinkedList<BlockIndex>();
            pumpLayerQueues.put(layer, pumpQueue);
        }

        return pumpQueue;
    }

    public void rebuildQueue() {
        numFluidBlocksFound = 0;
        pumpLayerQueues.clear();
        int x = xCoord;
        int y = aimY;
        int z = zCoord;
        Fluid pumpingFluid = BlockUtil.getFluid(worldObj.getBlock(x, y, z));

        if (pumpingFluid == null) {
            return;
        }

        if (pumpingFluid != tank.getAcceptedFluid() && tank.getAcceptedFluid() != null) {
            return;
        }

        Set<BlockIndex> visitedBlocks = new HashSet<BlockIndex>();
        Deque<BlockIndex> fluidsFound = new LinkedList<BlockIndex>();

        queueForPumping(x, y, z, visitedBlocks, fluidsFound, pumpingFluid);

        //      long timeoutTime = System.nanoTime() + 10000;

        while (!fluidsFound.isEmpty()) {
            Deque<BlockIndex> fluidsToExpand = fluidsFound;
            fluidsFound = new LinkedList<BlockIndex>();

            for (BlockIndex index : fluidsToExpand) {
                queueForPumping(index.x, index.y + 1, index.z, visitedBlocks, fluidsFound, pumpingFluid);
                queueForPumping(index.x + 1, index.y, index.z, visitedBlocks, fluidsFound, pumpingFluid);
                queueForPumping(index.x - 1, index.y, index.z, visitedBlocks, fluidsFound, pumpingFluid);
                queueForPumping(index.x, index.y, index.z + 1, visitedBlocks, fluidsFound, pumpingFluid);
                queueForPumping(index.x, index.y, index.z - 1, visitedBlocks, fluidsFound, pumpingFluid);

                if (pumpingFluid == FluidRegistry.WATER && !BuildCraftCore.consumeWaterSources
                        && numFluidBlocksFound >= 9) {
                    return;
                }

                //            if (System.nanoTime() > timeoutTime)
                //               return;
            }
        }
    }

    public void queueForPumping(int x, int y, int z, Set<BlockIndex> visitedBlocks, Deque<BlockIndex> fluidsFound,
            Fluid pumpingFluid) {
        BlockIndex index = new BlockIndex(x, y, z);
        if (visitedBlocks.add(index)) {
            if ((x - xCoord) * (x - xCoord) + (z - zCoord) * (z - zCoord) > 64 * 64) {
                return;
            }

            Block block = worldObj.getBlock(x, y, z);

            if (BlockUtil.getFluid(block) == pumpingFluid) {
                fluidsFound.add(index);
            }

            if (canDrainBlock(block, x, y, z, pumpingFluid)) {
                getLayerQueue(y).add(index);
                numFluidBlocksFound++;
            }
        }
    }

    private boolean isPumpableFluid(int x, int y, int z) {
        Fluid fluid = BlockUtil.getFluid(worldObj.getBlock(x, y, z));

        if (fluid == null) {
            return false;
        } else if (!isFluidAllowed(fluid)) {
            return false;
        } else {
            return !(tank.getAcceptedFluid() != null && tank.getAcceptedFluid() != fluid);
        }
    }

    private boolean canDrainBlock(Block block, int x, int y, int z, Fluid fluid) {
        if (!isFluidAllowed(fluid)) {
            return false;
        }

        FluidStack fluidStack = BlockUtil.drainBlock(block, worldObj, x, y, z, false);

        if (fluidStack == null || fluidStack.amount <= 0) {
            return false;
        } else {
            return fluidStack.getFluid() == fluid;
        }
    }

    private boolean isFluidAllowed(Fluid fluid) {
        return BuildCraftFactory.pumpDimensionList.isFluidAllowed(fluid, worldObj.provider.dimensionId);
    }

    @Override
    public void readFromNBT(NBTTagCompound data) {
        super.readFromNBT(data);

        tank.readFromNBT(data);

        powered = data.getBoolean("powered");

        aimY = data.getInteger("aimY");
        tubeY = data.getFloat("tubeY");

        mjStored = data.getDouble("mjStored");
    }

    @Override
    public void writeToNBT(NBTTagCompound data) {
        super.writeToNBT(data);

        tank.writeToNBT(data);

        data.setBoolean("powered", powered);

        data.setInteger("aimY", aimY);

        if (tube != null) {
            data.setFloat("tubeY", (float) tube.posY);
        } else {
            data.setFloat("tubeY", yCoord);
        }

        data.setDouble("mjStored", mjStored);
    }

    @Override
    public boolean isActive() {
        BlockIndex next = getNextIndexToPump(false);

        if (next != null) {
            return isPumpableFluid(next.x, next.y, next.z);
        } else {
            return false;
        }
    }

    @Override
    public PacketPayload getPacketPayload() {
        PacketPayload payload = new PacketPayload(new PacketPayload.StreamWriter() {
            @Override
            public void writeData(ByteBuf buf) {
                buf.writeInt(aimY);
                buf.writeFloat((float) tubeY);
                buf.writeBoolean(powered);
            }
        });

        return payload;
    }

    @Override
    public void handleUpdatePacket(PacketUpdate packet) throws IOException {
        PacketPayload payload = packet.payload;
        ByteBuf data = payload.stream;
        aimY = data.readInt();
        tubeY = data.readFloat();
        powered = data.readBoolean();

        setTubePosition();
    }

    @Override
    public void handleDescriptionPacket(PacketUpdate packet) throws IOException {
        handleUpdatePacket(packet);
    }

    private void setTubePosition() {
        if (tube != null) {
            tube.iSize = CoreConstants.PIPE_MAX_POS - CoreConstants.PIPE_MIN_POS;
            tube.kSize = CoreConstants.PIPE_MAX_POS - CoreConstants.PIPE_MIN_POS;
            tube.jSize = yCoord - tube.posY;

            tube.setPosition(xCoord + CoreConstants.PIPE_MIN_POS, tubeY, zCoord + CoreConstants.PIPE_MIN_POS);
        }
    }

    @Override
    public void invalidate() {
        super.invalidate();
        destroy();
    }

    @Override
    public void validate() {
        tileBuffer = null;
        super.validate();
    }

    @Override
    public void destroy() {
        tileBuffer = null;
        pumpLayerQueues.clear();
        destroyTube();
    }

    @Override
    public boolean manageFluids() {
        return true;
    }

    @Override
    public boolean manageSolids() {
        return false;
    }

    @Override
    public boolean allowAction(IAction action) {
        return false;
    }

    // IFluidHandler implementation.
    @Override
    public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
        // not acceptable
        return 0;
    }

    @Override
    public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
        return tank.drain(maxDrain, doDrain);
    }

    @Override
    public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {
        if (resource == null) {
            return null;
        } else if (!resource.isFluidEqual(tank.getFluid())) {
            return null;
        } else {
            return drain(from, resource.amount, doDrain);
        }
    }

    @Override
    public boolean canFill(ForgeDirection from, Fluid fluid) {
        return false;
    }

    @Override
    public boolean canDrain(ForgeDirection from, Fluid fluid) {
        return true;
    }

    @Override
    public FluidTankInfo[] getTankInfo(ForgeDirection from) {
        return new FluidTankInfo[] { tank.getInfo() };
    }
}