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

    laserSat.readFromNBT(nbt.getCompoundTag("laser"));

    /*NBTTagList tagList = nbt.getTagList("Inventory");
    for (int i = 0; i < tagList.tagCount(); i++) {
    	NBTTagCompound tag = (NBTTagCompound) tagList.tagAt(i);
    	byte slot = tag.getByte("Slot");
    	if (slot >= 0 && slot < invBuffer.length) {
    		invBuffer[slot] = ItemStack.loadItemStackFromNBT(tag);
    	}
    }*/
    if (nbt.hasKey("GlassPane")) {
      NBTTagCompound tag = nbt.getCompoundTag("GlassPane");
      glassPanel = ItemStack.loadItemStackFromNBT(tag);
    }

    laserX = nbt.getInteger("laserX");
    laserZ = nbt.getInteger("laserZ");
    mode = MODE.values()[nbt.getByte("mode")];

    if (mode == MODE.SPIRAL) {
      xCenter = nbt.getInteger("CenterX");
      yCenter = nbt.getInteger("CenterY");
      radius = nbt.getInteger("radius");
      numSteps = nbt.getInteger("numSteps");
      prevDir = ForgeDirection.values()[nbt.getInteger("prevDir")];
    }
  }
 @Override
 public void onChunkUnload() {
   if (laserSat != null) {
     laserSat.deactivateLaser();
   }
   isRunning = false;
 }
  /**
   * Checks to see if the situation for firing the laser exists... and changes the state accordingly
   */
  public void checkCanRun() {
    // Laser requires lense, redstone power, not be jammed, and be in orbit and energy to function
    if (!worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord)
        || glassPanel == null
        || storage.getEnergyStored()
            == 0 /*|| !(this.worldObj.provider instanceof IOrbitDimension)*/) {
      if (laserSat.isAlive()) {
        laserSat.deactivateLaser();
      }

      isRunning = false;
    } else if (!laserSat.isAlive()
        && !finished
        && !laserSat.getJammed()
        && worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord)
        && canMachineSeeEarth()) {

      // Laser will be on at this point
      int orbitDimId =
          0; // =
             // WorldUtil.getProviderForName(((IOrbitDimension)this.worldObj.provider).getPlanetToOrbit()).dimensionId;
      WorldServer orbitWorld = DimensionManager.getWorld(orbitDimId);

      if (ticket == null) {
        ticket =
            ForgeChunkManager.requestTicket(AdvancedRocketry.instance, this.worldObj, Type.NORMAL);
        if (ticket != null)
          ForgeChunkManager.forceChunk(
              ticket,
              new ChunkCoordIntPair(
                  this.xCoord / 16 - (this.xCoord < 0 ? 1 : 0),
                  this.zCoord / 16 - (this.zCoord < 0 ? 1 : 0)));
      }

      isRunning = laserSat.activateLaser(orbitWorld, laserX, laserZ);
    }

    if (!this.worldObj.isRemote)
      PacketHandler.sendToNearby(
          new PacketMachine(this, (byte) 2),
          this.xCoord,
          this.yCoord,
          this.zCoord,
          128,
          this.worldObj.provider.dimensionId);
  }
  /**
   * Take items from internal inventory
   *
   * @return
   */
  public void attempUnjam() {
    if (!laserSat.getJammed()) return;

    /*IInventory depositInv = getAvalibleInv(ZUtils.getFirstFilledSlot(invBuffer));

    //Assign subInv


    while(depositInv != null) {
    	ZUtils.mergeInventory(invBuffer, depositInv);

    	if(ZUtils.isInvEmpty(invBuffer))
    		break;
    	depositInv = getAvalibleInv(ZUtils.getFirstFilledSlot(invBuffer));
    }*/

    // TODO: finish
    laserSat.setJammed(false);
    // !ZUtils.isInvEmpty(invBuffer);
    finished = false;

    checkCanRun();
  }
  @Override
  public void updateEntity() {
    // TODO: drain energy
    if (!this.worldObj.isRemote) {
      tickSinceLastOperation++;
    }

    if (hasPowerForOperation()
        && isReadyForOperation()
        && laserSat.isAlive()
        && !laserSat.getJammed()) {
      laserSat.performOperation();

      storage.setEnergyStored(storage.getEnergyStored() - POWER_PER_OPERATION);
      tickSinceLastOperation = 0;
    }

    if (laserSat.isFinished()) {
      this.isRunning = false;
      laserSat.deactivateLaser();

      if (!laserSat.getJammed()) {
        if (mode == MODE.SINGLE) finished = true;

        if (this.worldObj.getBlockPowerInput(this.xCoord, this.yCoord, this.zCoord) != 0) {
          if (mode == MODE.LINE_X) {
            this.laserX += 3;
          } else if (mode == MODE.LINE_Z) {
            this.laserZ += 3;
          } else if (mode == MODE.SPIRAL) {
            numSteps++;
            if (radius < numSteps) {
              numSteps = 0;
              if (prevDir == ForgeDirection.NORTH) prevDir = ForgeDirection.EAST;
              else if (prevDir == ForgeDirection.EAST) {
                prevDir = ForgeDirection.SOUTH;
                radius++;
              } else if (prevDir == ForgeDirection.SOUTH) prevDir = ForgeDirection.WEST;
              else {
                prevDir = ForgeDirection.NORTH;
                radius++;
              }
            }

            this.laserX += 3 * prevDir.offsetX;
            this.laserZ += 3 * prevDir.offsetZ;
          }
        }
        // TODO: unneeded?
        checkCanRun();
      }
    }
  }
  @Override
  public void writeToNBT(NBTTagCompound nbt) {
    super.writeToNBT(nbt);
    storage.writeToNBT(nbt);

    NBTTagCompound laser = new NBTTagCompound();
    laserSat.writeToNBT(laser);

    nbt.setTag("laser", laser);

    NBTTagList list = new NBTTagList();
    NBTTagList itemList = new NBTTagList();
    /*for(int i = 0; i < invBuffer.length; i++)
    {
    	ItemStack stack = invBuffer[i];
    	if(stack != null) {
    		NBTTagCompound tag = new NBTTagCompound();
    		tag.setByte("Slot", (byte)(i));
    		stack.writeToNBT(tag);
    		itemList.appendTag(tag);
    	}
    }
    nbt.setTag("InventoryBuffer", itemList);*/

    if (glassPanel != null) {
      NBTTagCompound tag = new NBTTagCompound();
      glassPanel.writeToNBT(tag);
      nbt.setTag("GlassPane", tag);
    }

    nbt.setInteger("laserX", laserX);
    nbt.setInteger("laserZ", laserZ);
    nbt.setByte("mode", (byte) mode.ordinal());

    if (mode == MODE.SPIRAL) {
      nbt.setInteger("CenterX", xCenter);
      nbt.setInteger("CenterY", yCenter);
      nbt.setInteger("radius", radius);
      nbt.setInteger("numSteps", numSteps);
      nbt.setInteger("prevDir", prevDir.ordinal());
    }
  }
 public void setJammed(boolean b) {
   laserSat.setJammed(b);
 }
 public boolean isJammed() {
   return laserSat.getJammed();
 }
 public void onDestroy() {
   if (laserSat != null) {
     laserSat.deactivateLaser();
   }
   ForgeChunkManager.releaseTicket(ticket);
 }