// Skip water/lava
public int apply(int[] data) throws MaxChangedBlocksException {
        checkNotNull(data);
        BlockVector3 minY = region.getMinimumPoint();
        int originX = minY.getBlockX();
        int originY = minY.getBlockY();
        int originZ = minY.getBlockZ();
        int maxY = region.getMaximumPoint().getBlockY();
        BlockState fillerAir = BlockTypes.AIR.getDefaultState();
        int blocksChanged = 0;
        BlockState tmpBlock = BlockTypes.AIR.getDefaultState();
        // Apply heightmap
        int index = 0;
        for (int z = 0; z < height; ++z) {
            int zr = z + originZ;
            for (int x = 0; x < width; ++x, index++) {
                if (this.invalid != null && this.invalid[index]) {
                    continue;
                }
                int curHeight = this.data[index];
                // Clamp newHeight within the selection area
                int newHeight = Math.min(maxY, data[index]);
                // Offset x,z to be 'real' coordinates
                int xr = x + originX;
                // Depending on growing or shrinking we need to start at the bottom or top
                if (newHeight > curHeight) {
                    // Set the top block of the column to be the same type (this might go wrong with rounding)
                    BlockState existing = session.getBlock(xr, curHeight, zr);
                    // Skip water/lava
                    if (existing.getBlockType().getMaterial().isMovementBlocker()) {
                        int y0 = newHeight - 1;
                        for (int setY = y0, getY = curHeight - 1; setY >= curHeight; setY--, getY--) {
                            BlockState get;
                            if (getY >= 0 && getY < 256) {
                                get = session.getBlock(xr, getY, zr);
                            } else {
                                get = BlockTypes.AIR.getDefaultState();
                            }
                            if (get != BlockTypes.AIR.getDefaultState()) {
                                tmpBlock = get;
                            }
                            session.setBlock(xr, setY, zr, tmpBlock);
                            ++blocksChanged;
                        }
                        session.setBlock(xr, newHeight, zr, existing);
                        ++blocksChanged;
                    }
                } else if (curHeight > newHeight) {
                    // Set the top block of the column to be the same type
                    // (this could otherwise go wrong with rounding)
                    session.setBlock(xr, newHeight, zr, session.getBlock(xr, curHeight, zr));
                    ++blocksChanged;
                    // Fill rest with air
                    for (int y = newHeight + 1; y <= curHeight; ++y) {
                        session.setBlock(xr, y, zr, fillerAir);
                        ++blocksChanged;
                    }
                }
            }
        }
        // Drop trees to the floor -- TODO
        return blocksChanged;
    }
// Set the top block of the column to be the same type (this might go wrong with rounding)
BlockState existing = session.getBlock(xr, curHeight, zr);
// Skip water/lava
if (existing.getBlockType().getMaterial().isMovementBlocker()) {
int y0 = newHeight - 1;
}
int curHeight = this.data[index];
// Clamp newHeight within the selection area
int newHeight = Math.min(maxY, data[index]);
// Offset x,z to be 'real' coordinates
int xr = x + originX;
// Depending on growing or shrinking we need to start at the bottom or top
if (newHeight > curHeight) {
// Set the top block of the column to be the same type (this might go wrong with rounding)
BlockState existing = session.getBlock(xr, curHeight, zr);
// Skip water/lava
if (existing.getBlockType().getMaterial().isMovementBlocker()) {
int y0 = newHeight - 1;
for (int setY = y0, getY = curHeight - 1; setY >= curHeight; setY--, getY--) {
BlockState get;
if (getY >= 0 && getY < 256) {
get = session.getBlock(xr, getY, zr);
} else {
get = BlockTypes.AIR.getDefaultState();
}
if (get != BlockTypes.AIR.getDefaultState()) {
BlockState fillerAir = BlockTypes.AIR.getDefaultState();
int blocksChanged = 0;
BlockState tmpBlock = BlockTypes.AIR.getDefaultState();
// Apply heightmap
int index = 0;
for (int z = 0; z < height; ++z) {
int zr = z + originZ;
for (int x = 0; x < width; ++x, index++) {
if (this.invalid != null && this.invalid[index]) {
continue;
}
int curHeight = this.data[index];
// Clamp newHeight within the selection area
int newHeight = Math.min(maxY, data[index]);
// Offset x,z to be 'real' coordinates
int xr = x + originX;
// Depending on growing or shrinking we need to start at the bottom or top
if (newHeight > curHeight) {
// Set the top block of the column to be the same type (this might go wrong with rounding)
BlockState existing = session.getBlock(xr, curHeight, zr);
// Skip water/lava
if (existing.getBlockType().getMaterial().isMovementBlocker()) {
int y0 = newHeight - 1;
for (int setY = y0, getY = curHeight - 1; setY >= curHeight; setY--, getY--) {
BlockState get;
if (getY >= 0 && getY < 256) {
get = session.getBlock(xr, getY, zr);
} else {
get = BlockTypes.AIR.getDefaultState();
}
if (get != BlockTypes.AIR.getDefaultState()) {
tmpBlock = get;
}
session.setBlock(xr, setY, zr, tmpBlock);
++blocksChanged;
}
session.setBlock(xr, newHeight, zr, existing);
++blocksChanged;
}
} else if (curHeight > newHeight) {
// Set the top block of the column to be the same type