package tc.oc.commons.bukkit.util; import java.util.Collection; import gnu.trove.set.TLongSet; import gnu.trove.set.hash.TLongHashSet; import org.bukkit.EntityLocation; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.util.BlockVector; import org.bukkit.util.Vector; public class BlockUtils { public static BlockVector position(BlockState block) { return new BlockVector(block.getX(), block.getY(), block.getZ()); } public static Location center(Location location) { Location center = location.clone(); center.setX(center.getBlockX() + 0.5); center.setY(center.getBlockY() + 0.5); center.setZ(center.getBlockZ() + 0.5); return center; } public static EntityLocation center(EntityLocation location) { EntityLocation center = location.clone(); center.setX(center.getBlockX() + 0.5); center.setY(center.getBlockY() + 0.5); center.setZ(center.getBlockZ() + 0.5); return center; } public static Location center(Block block) { return center(block.getLocation()); } public static Location center(BlockState state) { return center(state.getLocation()); } /** * Return the "base" {@link Location} of the block at the given location, * which is the bottom center point on the block (i.e. the location of any * block-shaped entity that is aligned with the block). */ public static Location base(Location location) { Location center = location.clone(); center.setX(center.getBlockX() + 0.5); center.setY(center.getBlockY()); center.setZ(center.getBlockZ() + 0.5); return center; } public static Location base(Block block) { return base(block.getLocation()); } public static Location base(BlockState state) { return base(state.getLocation()); } public static boolean isInside(Vector point, Location blockLocation) { return blockLocation.getX() <= point.getX() && point.getX() <= blockLocation.getX() + 1 && blockLocation.getY() <= point.getY() && point.getY() <= blockLocation.getY() + 1 && blockLocation.getZ() <= point.getZ() && point.getZ() <= blockLocation.getZ() + 1; } /** * BlockVector encoding API - pack a BlockVector into a single long */ private static final int SHIFT = 21; private static final long MASK = ~(-1 << SHIFT); private static final long SIGN_MASK = 1 << (SHIFT - 1); /** * Decode a single component from the packed coordinates */ private static long unpack(long packed, int shift) { packed >>= shift; // Sign extension if((packed & SIGN_MASK) == 0) { packed &= MASK; } else { packed |= ~MASK; } return packed; } public static BlockVector decodePos(long from, BlockVector to) { to.setX(unpack(from, 0)); to.setY(unpack(from, SHIFT)); to.setZ(unpack(from, SHIFT + SHIFT)); return to; } public static BlockVector decodePos(long encoded) { return new BlockVector( unpack(encoded, 0), unpack(encoded, SHIFT), unpack(encoded, SHIFT + SHIFT) ); } public static final long ENCODED_NULL_POS = Long.MIN_VALUE; public static long encodePos(long x, long y, long z) { return (x & MASK) | ((y & MASK) << SHIFT) | ((z & MASK) << (SHIFT + SHIFT)); } public static long encodePos(BlockVector vector) { return encodePos(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); } public static long encodePos(Block block) { return encodePos(block.getX(), block.getY(), block.getZ()); } public static long encodePos(BlockState block) { return encodePos(block.getX(), block.getY(), block.getZ()); } public static TLongSet encodePosSet(Collection<?> vectors) { TLongSet encoded = new TLongHashSet(vectors.size()); for(Object o : vectors) { if(o instanceof BlockVector) { encoded.add(encodePos((BlockVector) o)); } } return encoded; } /** * Return the encoded location neighboring the given location on the given side. * Equivalent to {@link Block#getRelative}. */ public static long neighborPos(long encoded, BlockFace face) { return encodePos( unpack(encoded, 0) + face.getModX(), unpack(encoded, SHIFT) + face.getModY(), unpack(encoded, SHIFT + SHIFT) + face.getModZ() ); } public static Block blockAt(World world, Vector pos) { return world.getBlockAt(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); } /** * Return the {@link Block} in the given {@link World}, at the given encoded location. * This method is more efficient than creating an intermediate {@link BlockVector}, * and more convenient. */ public static Block blockAt(World world, long encoded) { return world.getBlockAt( (int) unpack(encoded, 0), (int) unpack(encoded, SHIFT), (int) unpack(encoded, SHIFT + SHIFT) ); } }