Package buildcraft.factory

Source Code of buildcraft.factory.TilePump

/**
* 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.BlockIndex;
import buildcraft.api.core.SafeTimeTracker;
import buildcraft.api.tiles.IHasWork;
import buildcraft.core.CoreConstants;
import buildcraft.core.EntityBlock;
import buildcraft.core.RFBattery;
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 IHasWork, 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;

  public TilePump() {
    super();
    this.setBattery(new RFBattery(1000, 150, 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 (getBattery().useEnergy(100, 100, false) > 0) {
          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");
  }

  @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);
    }
  }

  @Override
  public boolean hasWork() {
    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();
  }

  // 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()};
  }
}
TOP

Related Classes of buildcraft.factory.TilePump

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.