gt: 		TileEntity te = getAdjacentTileEntity(start, direction);
context: package cofh.lib.util.position;
  
  import cofh.lib.util.helpers.BlockHelper;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.List;
  
  import net.minecraft.block.Block;
  import net.minecraft.nbt.NBTTagCompound;
  import net.minecraft.tileentity.TileEntity;
  import net.minecraft.world.World;
  import net.minecraftforge.common.util.ForgeDirection;
  
  public class BlockPosition implements Comparable<BlockPosition>, Serializable {
  
  	private static final long serialVersionUID = 8671402745765780610L;
  
  	public int x;
  	public int y;
  	public int z;
  	public ForgeDirection orientation;
  
  	public BlockPosition(int x, int y, int z) {
  
  		this.x = x;
  		this.y = y;
  		this.z = z;
  		orientation = ForgeDirection.UNKNOWN;
  	}
  
  	public BlockPosition(int x, int y, int z, ForgeDirection orientation) {
  
  		this.x = x;
  		this.y = y;
  		this.z = z;
  		this.orientation = orientation;
  	}
  
  	public BlockPosition(BlockPosition p) {
  
  		x = p.x;
  		y = p.y;
  		z = p.z;
  		orientation = p.orientation;
  	}
  
  	public BlockPosition(NBTTagCompound tag) {
  
  		x = tag.getInteger("bp_i");
  		y = tag.getInteger("bp_j");
  		z = tag.getInteger("bp_k");
  
  		if (!tag.hasKey("bp_dir")) {
  			orientation = ForgeDirection.UNKNOWN;
  		} else {
  			orientation = ForgeDirection.getOrientation(tag.getByte("bp_dir"));
  		}
  	}
  
  	public BlockPosition(TileEntity tile) {
  
  		x = tile.xCoord;
  		y = tile.yCoord;
  		z = tile.zCoord;
  		if (tile instanceof IRotateableTile) {
  			orientation = ((IRotateableTile) tile).getDirectionFacing();
  		} else {
  			orientation = ForgeDirection.UNKNOWN;
  		}
  	}
  
  	public static <T extends TileEntity & IRotateableTile> BlockPosition fromRotateableTile(T te) {
  
  		return new BlockPosition(te);
  	}
  
  	public BlockPosition copy() {
  
  		return new BlockPosition(x, y, z, orientation);
  	}
  
  	public BlockPosition copy(ForgeDirection orientation) {
  
  		return new BlockPosition(x, y, z, orientation);
  	}
  
  	public BlockPosition setOrientation(ForgeDirection o) {
  
  		orientation = o;
  		return this;
  	}
  
  	public BlockPosition step(int dir) {
  
  		int[] d = BlockHelper.SIDE_COORD_MOD[dir];
  		x += d[0];
  		y += d[1];
  		z += d[2];
  		return this;
  	}
  
  	public BlockPosition step(int dir, int dist) {
  
  		int[] d = BlockHelper.SIDE_COORD_MOD[dir];
  		x += d[0] * dist;
  		y += d[1] * dist;
  		z += d[2] * dist;
  		return this;
  	}
  
  	public BlockPosition step(ForgeDirection dir) {
  
  		x += dir.offsetX;
  		y += dir.offsetY;
  		z += dir.offsetZ;
  		return this;
  	}
  
  	public BlockPosition step(ForgeDirection dir, int dist) {
  
  		x += dir.offsetX * dist;
  		y += dir.offsetY * dist;
  		z += dir.offsetZ * dist;
  		return this;
  	}
  
  	public BlockPosition moveForwards(int step) {
  
  		switch (orientation) {
  		case UP:
  			y = y + step;
  			break;
  		case DOWN:
  			y = y - step;
  			break;
  		case SOUTH:
  			z = z + step;
  			break;
  		case NORTH:
  			z = z - step;
  			break;
  		case EAST:
  			x = x + step;
  			break;
  		case WEST:
  			x = x - step;
  			break;
  		default:
  		}
  		return this;
  	}
  
  	public BlockPosition moveBackwards(int step) {
  
  		return moveForwards(-step);
  	}
  
  	public BlockPosition moveRight(int step) {
  
  		switch (orientation) {
  		case UP:
  		case SOUTH:
  			x = x - step;
  			break;
  		case DOWN:
  		case NORTH:
  			x = x + step;
  			break;
  		case EAST:
  			z = z + step;
  			break;
  		case WEST:
  			z = z - step;
  			break;
  		default:
  			break;
  		}
  		return this;
  	}
  
  	public BlockPosition moveLeft(int step) {
  
  		return moveRight(-step);
  	}
  
  	public BlockPosition moveUp(int step) {
  
  		switch (orientation) {
  		case EAST:
  		case WEST:
  		case NORTH:
  		case SOUTH:
  			y = y + step;
  			break;
  		case UP:
  			z = z - step;
  			break;
  		case DOWN:
  			z = z + step;
  		default:
  			break;
  		}
  		return this;
  	}
  
  	public BlockPosition moveDown(int step) {
  
  		return moveUp(-step);
  	}
  
  	public void writeToNBT(NBTTagCompound tag) {
  
  		tag.setInteger("bp_i", x);
  		tag.setInteger("bp_j", y);
  		tag.setInteger("bp_k", z);
  		tag.setByte("bp_dir", (byte) orientation.ordinal());
  	}
  
  	@Override
  	public String toString() {
  
  		if (orientation == null) {
  			return "{" + x + ", " + y + ", " + z + "}";
  		}
  		return "{" + x + ", " + y + ", " + z + ";" + orientation.toString() + "}";
  	}
  
  	@Override
  	public boolean equals(Object obj) {
  
  		if (!(obj instanceof BlockPosition)) {
  			return false;
  		}
  		BlockPosition bp = (BlockPosition) obj;
  		return bp.x == x & bp.y == y & bp.z == z & bp.orientation == orientation;
  	}
  
  	// so compiler will optimize
  	public boolean equals(BlockPosition bp) {
  
  		return bp != null && bp.x == x & bp.y == y & bp.z == z & bp.orientation == orientation;
  	}
  
  	@Override
  	public int hashCode() {
  
  		return (x & 0xFFF) | (y & 0xFF << 8) | (z & 0xFFF << 12);
  	}
  
  	public BlockPosition min(BlockPosition p) {
  
  		return new BlockPosition(p.x > x ? x : p.x, p.y > y ? y : p.y, p.z > z ? z : p.z);
  	}
  
  	public BlockPosition max(BlockPosition p) {
  
  		return new BlockPosition(p.x < x ? x : p.x, p.y < y ? y : p.y, p.z < z ? z : p.z);
  	}
  
  	public List<BlockPosition> getAdjacent(boolean includeVertical) {
  
  		List<BlockPosition> a = new ArrayList<BlockPosition>(4 + (includeVertical ? 2 : 0));
  		a.add(copy(ForgeDirection.EAST).moveForwards(1));
  		a.add(copy(ForgeDirection.WEST).moveForwards(1));
  		a.add(copy(ForgeDirection.SOUTH).moveForwards(1));
  		a.add(copy(ForgeDirection.NORTH).moveForwards(1));
  		if (includeVertical) {
  			a.add(copy(ForgeDirection.UP).moveForwards(1));
  			a.add(copy(ForgeDirection.DOWN).moveForwards(1));
  		}
  		return a;
  	}
  
  	public boolean blockExists(World world) {
  
  		return world.blockExists(x, y, z);
  	}
  
  	public TileEntity getTileEntity(World world) {
  
  		return world.getTileEntity(x, y, z);
  	}
  
  	public Block getBlock(World world) {
  
  		return world.getBlock(x, y, z);
  	}
  
  	@SuppressWarnings("unchecked")
  	public <T> T getTileEntity(World world, Class<T> targetClass) {
  
  		TileEntity te = world.getTileEntity(x, y, z);
  		if (targetClass.isInstance(te)) {
  			return (T) te;
  		} else {
  			return null;
  		}
  	}
  
  	public static ForgeDirection getDirection(int xS, int yS, int zS, int x, int y, int z) {
  
  		int dir = 0;
  		if (y < yS) {
  			dir |= 1;
  		} else if (y != yS) {
  			dir |= 2;
  		}
  		if (z < zS) {
  			dir |= 4;
  		} else if (z != zS) {
  			dir |= 8;
  		}
  		if (x < xS) {
  			dir |= 16;
  		} else if (x != xS) {
  			dir |= 32;
  		}
  		switch (dir) {
  		case 2:
  			return ForgeDirection.UP;
  		case 1:
  			return ForgeDirection.DOWN;
  		case 4:
  			return ForgeDirection.WEST;
  		case 8:
  			return ForgeDirection.EAST;
  		case 16:
  			return ForgeDirection.NORTH;
  		case 32:
  			return ForgeDirection.SOUTH;
  		default:
  			return ForgeDirection.UNKNOWN;
  		}
  	}
  
  	public static TileEntity getTileEntityRaw(World world, int x, int y, int z) {
  
  		if (!world.blockExists(x, y, z)) {
  			return null;
  		}
  		return world.getChunkFromBlockCoords(x, z).getTileEntityUnsafe(x & 15, y, z & 15);
  	}
  
  	@SuppressWarnings("unchecked")
  	public static <T> T getTileEntityRaw(World world, int x, int y, int z, Class<T> targetClass) {
  
  		TileEntity te = getTileEntityRaw(world, x, y, z);
  		if (targetClass.isInstance(te)) {
  			return (T) te;
  		} else {
  			return null;
  		}
  	}
  
  	public static boolean blockExists(TileEntity start, ForgeDirection dir) {
  
  		final int x = start.xCoord + dir.offsetX, y = start.yCoord + dir.offsetY, z = start.zCoord + dir.offsetZ;
  		return start.getWorldObj().blockExists(x, y, z);
  	}
  
  	public static TileEntity getAdjacentTileEntity(TileEntity start, ForgeDirection dir) {
  
  		final int x = start.xCoord + dir.offsetX, y = start.yCoord + dir.offsetY, z = start.zCoord + dir.offsetZ;
  		return getTileEntityRaw(start.getWorldObj(), x, y, z);
  	}
  
  	@SuppressWarnings("unchecked")
  	public static <T> T getAdjacentTileEntity(TileEntity start, ForgeDirection direction, Class<T> targetClass) {
  
  