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);
      }
    }
  }
 private void verifyTileAt(CrystalNetworkTile te, WorldLocation loc) {
   UUID key = te.getUniqueID();
   WorldLocation prev = verifier.get(key);
   if (prev != null && !prev.equals(loc)) {
     te.getWorld().setBlockToAir(te.getX(), te.getY(), te.getZ());
     throw new InvalidLocationException(te, loc, prev);
   } else verifier.put(key, loc);
 }
 public void overloadColorConnectedTo(
     CrystalTransmitter te, CrystalElement color, int num, boolean recursive) {
   ArrayList<CrystalReceiver> li = this.getAllColorConnectedTo(te, color, recursive);
   if (li.isEmpty()) return;
   WeightedRandom<CrystalNetworkTile> w = new WeightedRandom();
   for (CrystalReceiver r : li) {
     if (r instanceof NaturalCrystalSource) continue;
     double wt = 1;
     if (r instanceof CrystalFuse) {
       wt = ((CrystalFuse) r).getFailureWeight(color);
     }
     w.addEntry(r, wt * 10);
   }
   for (int i = 0; i < num; i++) {
     CrystalNetworkTile tile = w.getRandomEntry();
     if (tile instanceof CrystalFuse) {
       ((CrystalFuse) tile).overload(color);
     } else {
       double x = tile.getX() + 0.5;
       double y = tile.getY() + 0.5;
       double z = tile.getZ() + 0.5;
       tile.getWorld().setBlock(tile.getX(), tile.getY(), tile.getZ(), Blocks.air);
       tile.getWorld().createExplosion(null, x, y, z, 1.5F + rand.nextFloat() * 1.5F, true);
     }
   }
 }
  public CrystalNetworkTile getNearestTileOfType(
      CrystalNetworkTile te, Class<? extends CrystalNetworkTile> type, int range) {
    CrystalNetworkTile ret = null;
    double dist = Double.POSITIVE_INFINITY;
    HashSet<WorldLocation> rem = new HashSet();
    for (WorldLocation c : tiles.keySet()) {
      CrystalNetworkTile tile = tiles.get(c);
      if (tile == null) {
        ChromatiCraft.logger.logError("Null tile at " + c + " but still cached?!");
        // c.setBlock(Blocks.brick_block);
        rem.add(c);
      } else if (tile == te) {

      } else if (te.getWorld().provider.dimensionId == c.dimensionID) {
        if (type.isAssignableFrom(tile.getClass())) {
          double d = tile.getDistanceSqTo(te.getX(), te.getY(), te.getZ());
          if (d <= range * range && d < dist) {
            dist = d;
            ret = tile;
          }
        }
      }
    }
    for (WorldLocation loc : rem) {
      tiles.remove(loc);
    }
    return ret;
  }
 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 Collection<CrystalNetworkTile> getNearTilesOfType(
     World world, int x, int y, int z, Class<? extends CrystalNetworkTile> type, int range) {
   Collection<CrystalNetworkTile> ret = new ArrayList();
   HashSet<WorldLocation> rem = new HashSet();
   for (WorldLocation c : tiles.keySet()) {
     CrystalNetworkTile tile = tiles.get(c);
     if (tile == null) {
       ChromatiCraft.logger.logError("Null tile at " + c + " but still cached?!");
       // c.setBlock(Blocks.brick_block);
       rem.add(c);
     } else if (world.provider.dimensionId == c.dimensionID) {
       if (type.isAssignableFrom(tile.getClass())) {
         double d = tile.getDistanceSqTo(x, y, z);
         if (d <= range * range) {
           ret.add(tile);
         }
       }
     }
   }
   for (WorldLocation loc : rem) {
     tiles.remove(loc);
   }
   return ret;
 }
 public Collection<CrystalNetworkTile> getNearTilesOfType(
     CrystalNetworkTile te, Class<? extends CrystalNetworkTile> type, int range) {
   return this.getNearTilesOfType(te.getWorld(), te.getX(), te.getY(), te.getZ(), type, range);
 }