1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00
azalea/azalea-core/src/position.rs
2022-06-25 02:33:28 -05:00

324 lines
7.5 KiB
Rust

use crate::ResourceLocation;
use azalea_buf::{McBufReadable, McBufWritable};
use std::{
io::{Read, Write},
ops::Rem,
};
pub trait PositionXYZ<T> {
fn add_x(&self, n: T) -> Self;
fn add_y(&self, n: T) -> Self;
fn add_z(&self, n: T) -> Self;
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl BlockPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
BlockPos { x, y, z }
}
}
impl Rem<i32> for BlockPos {
type Output = Self;
fn rem(self, rhs: i32) -> Self {
BlockPos {
x: self.x % rhs,
y: self.y % rhs,
z: self.z % rhs,
}
}
}
impl PositionXYZ<i32> for BlockPos {
fn add_x(&self, n: i32) -> Self {
BlockPos {
x: self.x + n,
y: self.y,
z: self.z,
}
}
fn add_y(&self, n: i32) -> Self {
BlockPos {
x: self.x,
y: self.y + n,
z: self.z,
}
}
fn add_z(&self, n: i32) -> Self {
BlockPos {
x: self.x,
y: self.y,
z: self.z + n,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct ChunkPos {
pub x: i32,
pub z: i32,
}
impl ChunkPos {
pub fn new(x: i32, z: i32) -> Self {
ChunkPos { x, z }
}
}
/// The coordinates of a chunk section in the world.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl ChunkSectionPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
ChunkSectionPos { x, y, z }
}
}
/// The coordinates of a block inside a chunk.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ChunkBlockPos {
pub x: u8,
pub y: i32,
pub z: u8,
}
impl ChunkBlockPos {
pub fn new(x: u8, y: i32, z: u8) -> Self {
ChunkBlockPos { x, y, z }
}
}
/// The coordinates of a block inside a chunk section.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionBlockPos {
/// A number between 0 and 16.
pub x: u8,
/// A number between 0 and 16.
pub y: u8,
/// A number between 0 and 16.
pub z: u8,
}
impl ChunkSectionBlockPos {
pub fn new(x: u8, y: u8, z: u8) -> Self {
ChunkSectionBlockPos { x, y, z }
}
}
/// A block pos with an attached dimension
#[derive(Debug, Clone)]
pub struct GlobalPos {
pub pos: BlockPos,
// this is actually a ResourceKey in Minecraft, but i don't think it matters?
pub dimension: ResourceLocation,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct EntityPos {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl PositionXYZ<f64> for EntityPos {
fn add_x(&self, n: f64) -> Self {
EntityPos {
x: self.x + n,
y: self.y,
z: self.z,
}
}
fn add_y(&self, n: f64) -> Self {
EntityPos {
x: self.x,
y: self.y + n,
z: self.z,
}
}
fn add_z(&self, n: f64) -> Self {
EntityPos {
x: self.x,
y: self.y,
z: self.z + n,
}
}
}
impl From<&BlockPos> for ChunkPos {
fn from(pos: &BlockPos) -> Self {
ChunkPos {
x: pos.x.div_floor(16),
z: pos.z.div_floor(16),
}
}
}
impl From<BlockPos> for ChunkSectionPos {
fn from(pos: BlockPos) -> Self {
ChunkSectionPos {
x: pos.x.div_floor(16),
y: pos.y.div_floor(16),
z: pos.z.div_floor(16),
}
}
}
impl From<ChunkSectionPos> for ChunkPos {
fn from(pos: ChunkSectionPos) -> Self {
ChunkPos { x: pos.x, z: pos.z }
}
}
impl From<&BlockPos> for ChunkBlockPos {
fn from(pos: &BlockPos) -> Self {
ChunkBlockPos {
x: pos.x.rem_euclid(16).abs() as u8,
y: pos.y,
z: pos.z.rem_euclid(16).abs() as u8,
}
}
}
impl From<&BlockPos> for ChunkSectionBlockPos {
fn from(pos: &BlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x.rem(16).abs() as u8,
y: pos.y.rem(16).abs() as u8,
z: pos.z.rem(16).abs() as u8,
}
}
}
impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
fn from(pos: &ChunkBlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x,
y: pos.y.rem(16).abs() as u8,
z: pos.z,
}
}
}
impl From<&EntityPos> for BlockPos {
fn from(pos: &EntityPos) -> Self {
BlockPos {
x: pos.x.floor() as i32,
y: pos.y.floor() as i32,
z: pos.z.floor() as i32,
}
}
}
impl From<&EntityPos> for ChunkPos {
fn from(pos: &EntityPos) -> Self {
ChunkPos::from(&BlockPos::from(pos))
}
}
impl McBufReadable for BlockPos {
fn read_from(buf: &mut impl Read) -> Result<Self, String> {
let val = u64::read_from(buf)?;
let x = (val >> 38) as i32;
let y = (val & 0xFFF) as i32;
let z = ((val >> 12) & 0x3FFFFFF) as i32;
Ok(BlockPos { x, y, z })
}
}
impl McBufReadable for GlobalPos {
fn read_from(buf: &mut impl Read) -> Result<Self, String> {
Ok(GlobalPos {
dimension: ResourceLocation::read_from(buf)?,
pos: BlockPos::read_from(buf)?,
})
}
}
impl McBufReadable for ChunkSectionPos {
fn read_from(buf: &mut impl Read) -> Result<Self, String> {
let long = i64::read_from(buf)?;
Ok(ChunkSectionPos {
x: (long >> 42) as i32,
y: (long << 44 >> 44) as i32,
z: (long << 22 >> 42) as i32,
})
}
}
impl McBufWritable for BlockPos {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let data = (((self.x & 0x3FFFFFF) as i64) << 38)
| (((self.z & 0x3FFFFFF) as i64) << 12)
| ((self.y & 0xFFF) as i64);
data.write_into(buf)
}
}
impl McBufWritable for GlobalPos {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
ResourceLocation::write_into(&self.dimension, buf)?;
BlockPos::write_into(&self.pos, buf)?;
Ok(())
}
}
impl McBufWritable for ChunkSectionPos {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let long = (((self.x & 0x3FFFFF) as i64) << 42)
| (self.y & 0xFFFFF) as i64
| (((self.z & 0x3FFFFF) as i64) << 20);
long.write_into(buf)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_block_pos_to_chunk_pos() {
let block_pos = BlockPos::new(5, 78, -2);
let chunk_pos = ChunkPos::from(&block_pos);
assert_eq!(chunk_pos, ChunkPos::new(0, -1));
}
#[test]
fn test_from_block_pos_to_chunk_block_pos() {
let block_pos = BlockPos::new(5, 78, -2);
let chunk_block_pos = ChunkBlockPos::from(&block_pos);
assert_eq!(chunk_block_pos, ChunkBlockPos::new(5, 78, 14));
}
#[test]
fn test_from_entity_pos_to_block_pos() {
let entity_pos = EntityPos {
x: 31.5,
y: 80.0,
z: -16.1,
};
let block_pos = BlockPos::from(&entity_pos);
assert_eq!(block_pos, BlockPos::new(31, 80, -17));
}
#[test]
fn test_from_entity_pos_to_chunk_pos() {
let entity_pos = EntityPos {
x: 31.5,
y: 80.0,
z: -16.1,
};
let chunk_pos = ChunkPos::from(&entity_pos);
assert_eq!(chunk_pos, ChunkPos::new(1, -2));
}
}