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 addTile(CrystalNetworkTile te) {
    if (FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) {
      WorldLocation loc = PylonFinder.getLocation(te);
      CrystalNetworkTile old = tiles.get(loc);
      if (old != null) { // cache cleaning; old TEs may get out of sync for things like charge
        this.removeTile(old);
      }
      tiles.put(loc, te);
      if (te instanceof TileEntityCrystalPylon) {
        this.addPylon((TileEntityCrystalPylon) te);
      }
      this.verifyTileAt(te, loc);

      for (NotifiedNetworkTile tile : notifyCache) {
        tile.onTileNetworkTopologyChange(te, false);
      }

      if (te instanceof NotifiedNetworkTile) {
        notifyCache.add((NotifiedNetworkTile) te);
      }

      WorldCrystalNetworkData.initNetworkData(te.getWorld()).setDirty(true);
      if (te instanceof TileEntityCrystalPylon) {
        PylonLocationData.initNetworkData(te.getWorld()).setDirty(true);
      }
    }
  }
 @SubscribeEvent
 public void clearOnUnload(WorldEvent.Unload evt) {
   PylonFinder.stopAllSearches();
   int dim = evt.world.provider.dimensionId;
   ChromatiCraft.logger.debug("Unloading dimension " + dim + ", clearing crystal network.");
   try {
     this.clear(dim);
     for (WorldLocation c : tiles.keySet()) {
       CrystalNetworkTile te = tiles.get(c);
       PylonFinder.removePathsWithTile(te);
       if (te instanceof CrystalTransmitter) ((CrystalTransmitter) te).clearTargets(true);
     }
     tiles.removeWorld(evt.world);
     for (TileEntityCache c : pylons.values()) c.removeWorld(evt.world);
   } catch (ConcurrentModificationException e) {
     ChromatiCraft.logger.logError(
         "Clearing the crystal network on world unload caused a CME. This is indicative of a deeper problem.");
     e.printStackTrace();
   }
 }
 public Collection<TileEntityCrystalPylon> getNearbyPylons(
     World world, int x, int y, int z, CrystalElement e, int range, boolean LOS) {
   TileEntityCache<TileEntityCrystalPylon> c = pylons.get(e);
   Collection<TileEntityCrystalPylon> li = new ArrayList();
   if (c != null) {
     for (WorldLocation loc : c.keySet()) {
       if (loc.dimensionID == world.provider.dimensionId && loc.getDistanceTo(x, y, z) <= range) {
         if (!LOS || PylonFinder.lineOfSight(world, x, y, z, loc.xCoord, loc.yCoord, loc.zCoord)) {
           li.add(c.get(loc));
         }
       }
     }
   }
   return li;
 }
 void recalculateLOS() {
   hasLOS = PylonFinder.lineOfSight(loc1, loc2);
 }