public void removeTile(CrystalNetworkTile te) {
    tiles.remove(PylonFinder.getLocation(te));

    if (te instanceof NotifiedNetworkTile) {
      notifyCache.remove(te);
    }
    for (NotifiedNetworkTile tile : notifyCache) {
      tile.onTileNetworkTopologyChange(te, true);
    }

    if (te instanceof TileEntityCrystalPylon) {
      TileEntityCrystalPylon tile = (TileEntityCrystalPylon) te;
      TileEntityCache<TileEntityCrystalPylon> c = pylons.get(tile.getColor());
      if (c != null) c.remove(tile);
    }
    Collection<CrystalFlow> li = flows.get(te.getWorld().provider.dimensionId);
    Iterator<CrystalFlow> it = li.iterator();
    while (it.hasNext()) {
      CrystalFlow p = it.next();
      if (p.contains(te)) {
        CrystalNetworkLogger.logFlowBreak(p, FlowFail.TILE);
        p.resetTiles();
        p.receiver.onPathBroken(p, FlowFail.TILE);
        it.remove();
      }
    }
    PylonFinder.removePathsWithTile(te);
    WorldCrystalNetworkData.initNetworkData(te.getWorld()).setDirty(true); // was false
    if (te instanceof TileEntityCrystalPylon) {
      PylonLocationData.initNetworkData(te.getWorld()).setDirty(true);
    }
  }
  public void tick(TickType type, Object... data) {
    for (int dim : flows.keySet()) {
      Collection<CrystalFlow> c = flows.get(dim);
      Iterator<CrystalFlow> it = c.iterator();
      while (it.hasNext()) {
        CrystalFlow p = it.next();

        if (toBreak.contains(p)) {
          p.receiver.onPathBroken(p, FlowFail.SIGHT); // sight
          p.resetTiles();
          it.remove();
        } else {
          if (p.transmitter.canConduct() && p.canTransmit()) {
            int amt = p.drain();
            if (amt > 0) {
              int add = p.receiver.receiveElement(p.element, amt);
              p.transmitter.drain(p.element, amt);
              if (p.isComplete()) {
                p.resetTiles();
                it.remove();
                CrystalNetworkLogger.logFlowSatisfy(p);
                p.receiver.onPathCompleted(p);
              } else if (add <= 0) {
                CrystalNetworkLogger.logFlowBreak(p, FlowFail.FULL);
                // p.receiver.onPathBroken(p.element);
                p.resetTiles();
                it.remove();
              }
            }
          } else {
            CrystalNetworkLogger.logFlowBreak(p, FlowFail.ENERGY);
            p.receiver.onPathBroken(p, FlowFail.ENERGY);
            p.resetTiles();
            it.remove();
          }
        }
      }
    }
    toBreak.clear();
    /*
    for (ChunkRequest cr : pathfindingChunkRequests) {
    	Chunk c = cr.chunk.load();
    	cr.pathfinder.receiveChunk(c);
    }
    pathfindingChunkRequests.clear();
     */
  }
 public void breakPaths(CrystalNetworkTile te) {
   Collection<CrystalFlow> li = flows.get(te.getWorld().provider.dimensionId);
   Iterator<CrystalFlow> it = li.iterator();
   while (it.hasNext()) {
     CrystalFlow p = it.next();
     if (p.contains(te)) {
       CrystalNetworkLogger.logFlowBreak(p, FlowFail.TILE);
       p.receiver.onPathBroken(p, FlowFail.TILE);
       p.resetTiles();
       it.remove();
     }
   }
 }
 public boolean makeRequest(
     CrystalReceiver r, CrystalElement e, int amount, World world, int range, int maxthru) {
   if (amount <= 0) return false;
   if (this.hasFlowTo(r, e, world)) return false;
   EntityPlayer ep = r.getPlacerUUID() != null ? world.func_152378_a(r.getPlacerUUID()) : null;
   CrystalFlow p = new PylonFinder(e, r, ep).findPylon(amount, maxthru);
   // ReikaJavaLibrary.pConsole(p, Side.SERVER);
   CrystalNetworkLogger.logRequest(r, e, amount, p);
   if (p != null) {
     flows.addValue(world.provider.dimensionId, p);
     p.transmitter.onUsedBy(ep, e);
     return true;
   }
   return false;
 }
  @SubscribeEvent
  public void markChunkCache(SetBlockEvent evt) {
    if (!evt.world.isRemote) {
      WorldChunk wc = new WorldChunk(evt.world, evt.chunkLocation);
      Collection<CrystalLink> c = losCache.get(wc);
      if (c != null) {
        for (CrystalLink l : c) {

          WorldLocation l1 = l.loc1;
          WorldLocation l2 = l.loc2;

          double[] angs =
              ReikaPhysicsHelper.cartesianToPolar(
                  l1.xCoord - l2.xCoord, l1.yCoord - l2.yCoord, l1.zCoord - l2.zCoord);
          double[] angs2 =
              ReikaPhysicsHelper.cartesianToPolar(
                  l1.xCoord - evt.xCoord, l1.yCoord - evt.yCoord, l1.zCoord - evt.zCoord);
          // Only check link if block near it
          if (ReikaMathLibrary.approxrAbs(angs[1], angs2[1], 3)
              && ReikaMathLibrary.approxrAbs(angs[2], angs2[2], 3)) {
            l.hasLOS = false;

            // Kill active flows if blocked
            for (CrystalFlow p : flows.get(evt.world.provider.dimensionId)) {
              if (!toBreak.contains(p)
                  && p.containsLink(l)
                  && !p.checkLineOfSight(l)) { // make only link check, not entire path
                CrystalNetworkLogger.logFlowBreak(p, FlowFail.SIGHT);
                this.schedulePathBreak(p);
              }
            }
          }
        }
      }
    }
  }