use std::{fmt::Debug, ops::RangeInclusive};
use anyhow::{ensure, Result};
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct BlockCoordinate {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl Debug for BlockCoordinate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("({}, {}, {})", self.x, self.y, self.z))
}
}
impl BlockCoordinate {
pub fn new(x: i32, y: i32, z: i32) -> Self {
Self { x, y, z }
}
#[inline]
pub fn offset(&self) -> ChunkOffset {
ChunkOffset {
x: self.x.rem_euclid(16) as u8,
y: self.y.rem_euclid(16) as u8,
z: self.z.rem_euclid(16) as u8,
}
}
#[inline]
pub fn chunk(&self) -> ChunkCoordinate {
ChunkCoordinate {
x: self.x.div_euclid(16),
y: self.y.div_euclid(16),
z: self.z.div_euclid(16),
}
}
pub fn try_delta(&self, x: i32, y: i32, z: i32) -> Option<BlockCoordinate> {
let x = self.x.checked_add(x)?;
let y = self.y.checked_add(y)?;
let z = self.z.checked_add(z)?;
Some(BlockCoordinate { x, y, z })
}
}
impl From<BlockCoordinate> for crate::protocol::coordinates::BlockCoordinate {
fn from(value: BlockCoordinate) -> Self {
crate::protocol::coordinates::BlockCoordinate {
x: value.x,
y: value.y,
z: value.z,
}
}
}
impl From<&crate::protocol::coordinates::BlockCoordinate> for BlockCoordinate {
fn from(value: &crate::protocol::coordinates::BlockCoordinate) -> Self {
BlockCoordinate {
x: value.x,
y: value.y,
z: value.z,
}
}
}
impl From<crate::protocol::coordinates::BlockCoordinate> for BlockCoordinate {
fn from(value: crate::protocol::coordinates::BlockCoordinate) -> Self {
(&value).into()
}
}
#[inline]
fn try_convert(value: f64) -> Result<i32> {
ensure!(value.is_finite(), "val was not finite");
ensure!(
value <= (i32::MAX as f64) && value >= (i32::MIN as f64),
"Value is out of bounds as i32"
);
Ok(value.round() as i32)
}
impl TryFrom<cgmath::Vector3<f64>> for BlockCoordinate {
type Error = anyhow::Error;
fn try_from(value: cgmath::Vector3<f64>) -> std::result::Result<Self, Self::Error> {
Ok(BlockCoordinate {
x: try_convert(value.x)?,
y: try_convert(value.y)?,
z: try_convert(value.z)?,
})
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct ChunkOffset {
pub x: u8,
pub y: u8,
pub z: u8,
}
impl ChunkOffset {
#[inline(always)]
fn debug_check(&self) {
debug_assert!(self.x < 16);
debug_assert!(self.y < 16);
debug_assert!(self.z < 16);
}
#[inline]
pub fn as_index(&self) -> usize {
self.debug_check();
256 * (self.z as usize) + 16 * (self.y as usize) + (self.x as usize)
}
#[inline]
pub fn from_index(index: usize) -> ChunkOffset {
assert!(index < 4096);
ChunkOffset {
x: (index % 16) as u8,
y: ((index / 16) % 16) as u8,
z: ((index / 256) % 16) as u8,
}
}
pub fn try_delta(&self, x: i8, y: i8, z: i8) -> Option<ChunkOffset> {
let x = self.x as i8 + x;
let y = self.y as i8 + y;
let z = self.z as i8 + z;
if !(0..16).contains(&x) || !(0..16).contains(&y) || !(0..16).contains(&z) {
None
} else {
Some(ChunkOffset {
x: x as u8,
y: y as u8,
z: z as u8,
})
}
}
}
impl Debug for ChunkOffset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("({}, {}, {})", self.x, self.y, self.z))
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct ChunkCoordinate {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl ChunkCoordinate {
pub fn new(x: i32, y: i32, z: i32) -> Self {
Self { x, y, z }
}
#[inline]
pub fn with_offset(&self, offset: ChunkOffset) -> BlockCoordinate {
offset.debug_check();
BlockCoordinate {
x: self.x * 16 + (offset.x as i32),
y: self.y * 16 + (offset.y as i32),
z: self.z * 16 + (offset.z as i32),
}
}
pub fn manhattan_distance(&self, other: ChunkCoordinate) -> u32 {
self.x
.abs_diff(other.x)
.saturating_add(self.y.abs_diff(other.y))
.saturating_add(self.z.abs_diff(other.z))
}
pub fn l_infinity_norm_distance(&self, other: ChunkCoordinate) -> u32 {
self.x
.abs_diff(other.x)
.max(self.y.abs_diff(other.y))
.max(self.z.abs_diff(other.z))
}
pub fn is_in_bounds(&self) -> bool {
const BOUNDS_RANGE: RangeInclusive<i32> = (i32::MIN / 16) ..= (i32::MAX / 16);
BOUNDS_RANGE.contains(&self.x) && BOUNDS_RANGE.contains(&self.y) && BOUNDS_RANGE.contains(&self.z)
}
pub fn try_delta(&self, x: i32, y: i32, z: i32) -> Option<ChunkCoordinate> {
let x = self.x.checked_add(x)?;
let y = self.y.checked_add(y)?;
let z = self.z.checked_add(z)?;
let candidate = ChunkCoordinate { x, y, z };
if candidate.is_in_bounds() {
Some(candidate)
} else {
None
}
}
}
impl Debug for ChunkCoordinate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("({}, {}, {})", self.x, self.y, self.z))
}
}
impl From<ChunkCoordinate> for crate::protocol::coordinates::ChunkCoordinate {
fn from(value: ChunkCoordinate) -> Self {
crate::protocol::coordinates::ChunkCoordinate {
x: value.x,
y: value.y,
z: value.z,
}
}
}
impl From<&crate::protocol::coordinates::ChunkCoordinate> for ChunkCoordinate {
fn from(value: &crate::protocol::coordinates::ChunkCoordinate) -> Self {
ChunkCoordinate {
x: value.x,
y: value.y,
z: value.z,
}
}
}
impl From<crate::protocol::coordinates::ChunkCoordinate> for ChunkCoordinate {
fn from(value: crate::protocol::coordinates::ChunkCoordinate) -> Self {
(&value).into()
}
}
impl TryFrom<cgmath::Vector3<f64>> for crate::protocol::coordinates::Vec3D {
type Error = anyhow::Error;
fn try_from(value: cgmath::Vector3<f64>) -> std::result::Result<Self, Self::Error> {
ensure!(
value.x.is_finite() && value.y.is_finite() && value.z.is_finite(),
"vec3D contained NaN or inf"
);
Ok(crate::protocol::coordinates::Vec3D {
x: value.x,
y: value.y,
z: value.z,
})
}
}
impl TryFrom<&crate::protocol::coordinates::Vec3D> for cgmath::Vector3<f64> {
type Error = anyhow::Error;
fn try_from(
value: &crate::protocol::coordinates::Vec3D,
) -> std::result::Result<Self, Self::Error> {
ensure!(
value.x.is_finite() && value.y.is_finite() && value.z.is_finite(),
"vec3D contained NaN or inf"
);
Ok(cgmath::Vector3 {
x: value.x,
y: value.y,
z: value.z,
})
}
}
impl TryFrom<crate::protocol::coordinates::Vec3D> for cgmath::Vector3<f64> {
type Error = anyhow::Error;
fn try_from(value: crate::protocol::coordinates::Vec3D) -> Result<Self> {
(&value).try_into()
}
}
#[derive(Copy, Clone, Debug)]
pub struct PlayerPositionUpdate {
pub tick: u64,
pub position: cgmath::Vector3<f64>,
pub velocity: cgmath::Vector3<f64>,
pub face_direction: (f64, f64),
}