@Override
  public void teleport(World world, double x, double y, double z) {
    Validator.isTrue(!deleted, "hologram already deleted");
    Validator.notNull(world, "world");

    updateLocation(world, x, y, z);

    if (this.world != world) {
      despawnEntities();
      refreshAll();
      return;
    }

    double currentY = y;
    boolean first = true;

    for (CraftHologramLine line : lines) {

      if (!line.isSpawned()) {
        continue;
      }

      currentY -= line.getHeight();

      if (first) {
        first = false;
      } else {
        currentY -= Configuration.spaceBetweenLines;
      }

      line.teleport(x, currentY, z);
    }
  }
  @Override
  public CraftItemLine insertItemLine(int index, ItemStack itemStack) {
    Validator.isTrue(!deleted, "hologram already deleted");
    Validator.notNull(itemStack, "itemStack");

    CraftItemLine line = new CraftItemLine(this, itemStack);
    lines.add(index, line);
    refreshSingleLines();
    return line;
  }
  private boolean addEntityToWorld(WorldServer nmsWorld, Entity nmsEntity) {
    Validator.isTrue(Bukkit.isPrimaryThread(), "Async entity add");

    if (validateEntityMethod == null) {
      return nmsWorld.addEntity(nmsEntity, SpawnReason.CUSTOM);
    }

    final int chunkX = MathHelper.floor(nmsEntity.locX / 16.0);
    final int chunkZ = MathHelper.floor(nmsEntity.locZ / 16.0);

    if (!nmsWorld.chunkProviderServer.isChunkLoaded(chunkX, chunkZ)) {
      // This should never happen
      nmsEntity.dead = true;
      return false;
    }

    nmsWorld.getChunkAt(chunkX, chunkZ).a(nmsEntity);
    nmsWorld.entityList.add(nmsEntity);

    try {
      validateEntityMethod.invoke(nmsWorld, nmsEntity);
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
    return true;
  }
  public void removeLine(CraftHologramLine line) {
    Validator.isTrue(!deleted, "hologram already deleted");

    lines.remove(line);
    line.despawn();
    refreshSingleLines();
  }
  @Override
  public void removeLine(int index) {
    Validator.isTrue(!deleted, "hologram already deleted");

    lines.remove(index).despawn();
    refreshSingleLines();
  }
  public CraftHologram(Location location) {
    Validator.notNull(location, "location");
    updateLocation(location.getWorld(), location.getX(), location.getY(), location.getZ());

    lines = Utils.newList();
    allowPlaceholders = false;
    creationTimestamp = System.currentTimeMillis();
    visibilityManager = new CraftVisibilityManager(this);
  }
  @Override
  public CraftTextLine insertTextLine(int index, String text) {
    Validator.isTrue(!deleted, "hologram already deleted");

    CraftTextLine line = new CraftTextLine(this, text);
    lines.add(index, line);
    refreshSingleLines();
    return line;
  }
  private void updateLocation(World world, double x, double y, double z) {
    Validator.notNull(world, "world");

    this.world = world;
    this.x = x;
    this.y = y;
    this.z = z;
    chunkX = Utils.floor(x) >> 4;
    chunkZ = Utils.floor(z) >> 4;
  }
  /** Forces the entities to spawn, without checking if the chunk is loaded. */
  public void spawnEntities() {
    Validator.isTrue(!deleted, "hologram already deleted");

    despawnEntities();

    double currentY = this.y;
    boolean first = true;

    for (CraftHologramLine line : lines) {

      currentY -= line.getHeight();

      if (first) {
        first = false;
      } else {
        currentY -= Configuration.spaceBetweenLines;
      }

      line.spawn(world, x, currentY, z);
      if (allowPlaceholders && line instanceof CraftTextLine) {
        PlaceholdersManager.trackIfNecessary((CraftTextLine) line);
      }
    }
  }
 @Override
 public void teleport(Location location) {
   Validator.notNull(location, "location");
   teleport(location.getWorld(), location.getX(), location.getY(), location.getZ());
 }