valence/src/block_pos.rs

133 lines
3.2 KiB
Rust

use std::io::{Read, Write};
use anyhow::bail;
use vek::Vec3;
use crate::protocol::{Decode, Encode};
/// Represents an absolute block position in a world.
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Debug)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl BlockPos {
/// Constructs a new block position.
pub const fn new(x: i32, y: i32, z: i32) -> Self {
Self { x, y, z }
}
/// Returns the block position a point is contained within.
pub fn at(pos: impl Into<Vec3<f64>>) -> Self {
pos.into().floor().as_::<i32>().into()
}
}
impl Encode for BlockPos {
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
match (self.x, self.y, self.z) {
(-0x2000000..=0x1ffffff, -0x800..=0x7ff, -0x2000000..=0x1ffffff) => {
let (x, y, z) = (self.x as u64, self.y as u64, self.z as u64);
(x << 38 | z << 38 >> 26 | y & 0xfff).encode(w)
}
_ => bail!("out of range: {self:?}"),
}
}
}
impl Decode for BlockPos {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
// Use arithmetic right shift to determine sign.
let val = i64::decode(r)?;
let x = val >> 38;
let z = val << 26 >> 38;
let y = val << 52 >> 52;
Ok(Self {
x: x as i32,
y: y as i32,
z: z as i32,
})
}
}
impl From<(i32, i32, i32)> for BlockPos {
fn from((x, y, z): (i32, i32, i32)) -> Self {
BlockPos::new(x, y, z)
}
}
impl From<BlockPos> for (i32, i32, i32) {
fn from(pos: BlockPos) -> Self {
(pos.x, pos.y, pos.z)
}
}
impl From<[i32; 3]> for BlockPos {
fn from([x, y, z]: [i32; 3]) -> Self {
BlockPos::new(x, y, z)
}
}
impl From<BlockPos> for [i32; 3] {
fn from(pos: BlockPos) -> Self {
[pos.x, pos.y, pos.z]
}
}
impl From<Vec3<i32>> for BlockPos {
fn from(pos: Vec3<i32>) -> Self {
Self::new(pos.x, pos.y, pos.z)
}
}
impl From<BlockPos> for Vec3<i32> {
fn from(pos: BlockPos) -> Self {
Vec3::new(pos.x, pos.y, pos.z)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn position() {
let xzs = [
(-33554432, true),
(-33554433, false),
(33554431, true),
(33554432, false),
(0, true),
(1, true),
(-1, true),
];
let ys = [
(-2048, true),
(-2049, false),
(2047, true),
(2048, false),
(0, true),
(1, true),
(-1, true),
];
let mut buf = [0; 8];
for (x, x_valid) in xzs {
for (y, y_valid) in ys {
for (z, z_valid) in xzs {
let pos = BlockPos::new(x, y, z);
if x_valid && y_valid && z_valid {
pos.encode(&mut &mut buf[..]).unwrap();
assert_eq!(BlockPos::decode(&mut &buf[..]).unwrap(), pos);
} else {
assert!(pos.encode(&mut &mut buf[..]).is_err());
}
}
}
}
}
}