diff --git a/Cargo.toml b/Cargo.toml index 70ce7f8..1733f53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ flume = "0.10" futures = "0.3" hematite-nbt = "0.5" log = "0.4" -nalgebra-glm = "0.17" num = "0.4" parking_lot = "0.12" paste = "1" @@ -39,6 +38,7 @@ sha2 = "0.10" thiserror = "1" tokio = { version = "1", features = ["full"] } uuid = "1" +vek = "0.15" [dependencies.reqwest] version = "0.11" diff --git a/examples/basic.rs b/examples/basic.rs index cbd9e32..cbfcff1 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -7,8 +7,8 @@ use valence::client::GameMode; use valence::config::{Config, ServerListPing}; use valence::text::Color; use valence::{ - async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text, - TextFormat, WorldId, WorldsMut, + async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text, TextFormat, + WorldId, WorldsMut, }; pub fn main() -> ShutdownResult { diff --git a/src/aabb.rs b/src/aabb.rs deleted file mode 100644 index 184656c..0000000 --- a/src/aabb.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::cmp::Ordering; -use std::hash::{Hash, Hasher}; - -use num::cast::AsPrimitive; - -use crate::glm::{self, Number, RealNumber, TVec, TVec3}; - -/// An Axis-aligned bounding box in an arbitrary dimension, defined by its -/// minimum and maximum corners. -/// -/// This type maintains the invariant that `min <= max` componentwise. -/// -/// The generic type `T` can be an integer, which is useful for AABBs on grids. -#[derive(Clone, Copy, Debug)] -pub struct Aabb { - min: TVec, - max: TVec, -} - -impl Aabb { - pub fn new(p0: impl Into>, p1: impl Into>) -> Self { - let p0 = p0.into(); - let p1 = p1.into(); - Self { - min: glm::min2(&p0, &p1), - max: glm::max2(&p0, &p1), - } - } - - pub fn point(pos: impl Into>) -> Self { - let pos = pos.into(); - Self { min: pos, max: pos } - } - - pub fn min(&self) -> TVec { - self.min - } - - pub fn max(&self) -> TVec { - self.max - } - - pub fn dimensions(&self) -> TVec { - self.max - self.min - } - - /// Moves this AABB by some vector. - pub fn translate(&self, v: impl Into>) -> Self { - let v = v.into(); - Self { - min: self.min + v, - max: self.max + v, - } - } - - /// Calculates the AABB union, which is the smallest AABB completely - /// encompassing both AABBs. - pub fn union(&self, other: Self) -> Self { - Self { - min: glm::min2(&self.min, &other.min), - max: glm::max2(&self.max, &other.max), - } - } - - pub fn collides_with_aabb(&self, other: &Self) -> bool { - let l = glm::less_than_equal(&self.min, &other.max); - let r = glm::greater_than_equal(&self.max, &other.min); - glm::all(&l.zip_map(&r, |l, r| l && r)) - } -} - -impl Aabb -where - i32: AsPrimitive, -{ - /// Returns the center (centroid) of this AABB. - pub fn center(&self) -> TVec { - (self.min + self.max).map(|c| c / 2.as_()) - } -} - -impl Aabb { - /// Construct an AABB from a center (centroid) and the dimensions of the box - /// along each axis. - pub fn from_center_and_dimensions( - center: impl Into>, - dims: impl Into>, - ) -> Self { - let half = dims.into() * T::from_subset(&0.5); - let center = center.into(); - Self { - min: center - half, - max: center + half, - } - } - - pub fn collides_with_sphere( - &self, - center: impl Into>, - radius: impl Into, - ) -> bool { - self.distance_to_point(center.into()) <= radius.into() - } - - pub fn distance_to_point(&self, p: impl Into>) -> T { - let p = p.into(); - glm::distance(&p, &glm::clamp_vec(&p, &self.min, &self.max)) - } -} - -impl Aabb -where - i32: AsPrimitive, -{ - pub fn surface_area(&self) -> T { - let d = self.dimensions(); - (d.x * d.y + d.y * d.z + d.z * d.x) * 2.as_() - } -} - -impl Aabb { - /// Constructs an AABB from a position and the dimensions of the box along - /// each axis. The position is the center of the bottom face of the AABB. - pub fn from_bottom_and_dimensions( - bottom: impl Into>, - dims: impl Into>, - ) -> Self { - let dims = dims.into(); - Self::from_center_and_dimensions(bottom, dims).translate([ - T::from_subset(&0.0), - dims.y * T::from_subset(&0.5), - T::from_subset(&0.0), - ]) - } -} - -impl Default for Aabb { - fn default() -> Self { - let d = T::default(); - Self::new([d; D], [d; D]) - } -} - -impl PartialEq for Aabb { - fn eq(&self, other: &Self) -> bool { - self.min == other.min && self.max == other.max - } -} - -impl Eq for Aabb {} - -impl PartialOrd for Aabb { - fn partial_cmp(&self, other: &Self) -> Option { - match self.min.partial_cmp(&other.min) { - Some(Ordering::Equal) => self.max.partial_cmp(&other.max), - ord => return ord, - } - } -} - -impl Hash for Aabb { - fn hash(&self, state: &mut H) { - self.min.hash(state); - self.max.hash(state); - } -} - -// TODO: impl Ord for Aabb -//impl Ord for Aabb { -// fn cmp(&self, other: &Self) -> std::cmp::Ordering { -// match self.min.cmp(&other.min) { -// Ordering::Equal => self.max.cmp(&other.max), -// ord => ord, -// } -// } -//} diff --git a/src/chunk.rs b/src/chunk.rs index 603fae1..2c4d3bf 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -5,13 +5,12 @@ use std::io::Write; use std::iter::FusedIterator; use std::ops::Deref; -use bitvec::bitvec; use bitvec::vec::BitVec; use num::Integer; use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator}; +use vek::Vec2; use crate::block::BlockState; -use crate::glm::DVec2; use crate::packets::play::{ BlockChange, ChunkDataAndUpdateLight, ChunkDataHeightmaps, ClientPlayPacket, MultiBlockChange, }; @@ -292,15 +291,6 @@ impl ChunkPos { pub const fn new(x: i32, z: i32) -> Self { Self { x, z } } - - /// Returns the chunk position of the chunk that the given coordinates are - /// contained within. - pub fn from_xz(xz: DVec2) -> Self { - Self { - x: (xz.x / 16.0) as i32, - z: (xz.y / 16.0) as i32, - } - } } impl From<(i32, i32)> for ChunkPos { diff --git a/src/client.rs b/src/client.rs index 544f04b..6275af2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,9 +3,9 @@ use std::iter::FusedIterator; use std::ops::Deref; use flume::{Receiver, Sender, TrySendError}; -use glm::DVec3; use rayon::iter::ParallelIterator; use uuid::Uuid; +use vek::Vec3; use crate::block_pos::BlockPos; use crate::config::{ @@ -26,9 +26,7 @@ use crate::server::ServerPacketChannels; use crate::slotmap::{Key, SlotMap}; use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance}; use crate::var_int::VarInt; -use crate::{ - glm, ident, ChunkPos, Chunks, Entities, EntityId, Server, Text, Ticks, LIBRARY_NAMESPACE, -}; +use crate::{ident, ChunkPos, Chunks, Entities, EntityId, Server, Text, Ticks, LIBRARY_NAMESPACE}; pub struct Clients { sm: SlotMap, @@ -117,8 +115,8 @@ pub struct Client { username: String, uuid: Uuid, on_ground: bool, - new_position: DVec3, - old_position: DVec3, + new_position: Vec3, + old_position: Vec3, /// Measured in degrees yaw: f32, /// Measured in degrees @@ -181,8 +179,8 @@ impl Client { username, uuid, on_ground: false, - new_position: DVec3::default(), - old_position: DVec3::default(), + new_position: Vec3::default(), + old_position: Vec3::default(), yaw: 0.0, pitch: 0.0, teleported_this_tick: false, @@ -216,7 +214,7 @@ impl Client { self.uuid } - pub fn position(&self) -> DVec3 { + pub fn position(&self) -> Vec3 { self.new_position } @@ -262,7 +260,7 @@ impl<'a> ClientMut<'a> { ClientMut(self.0) } - pub fn teleport(&mut self, pos: impl Into, yaw: f32, pitch: f32) { + pub fn teleport(&mut self, pos: impl Into>, yaw: f32, pitch: f32) { self.0.new_position = pos.into(); self.0.yaw = yaw; self.0.pitch = pitch; @@ -425,7 +423,10 @@ impl<'a> ClientMut<'a> { .map_or(2, |s| s.view_distance) .min(self.new_max_view_distance); - let center = ChunkPos::from_xz(self.0.new_position.xz()); + let center = ChunkPos::new( + (self.new_position.x / 16.0) as i32, + (self.new_position.z / 16.0) as i32, + ); // Send the update view position packet if the client changes the chunk section // they're in. @@ -483,20 +484,19 @@ impl<'a> ClientMut<'a> { // This is done after the chunks are loaded so that the "downloading terrain" // screen is closed at the appropriate time. - - // TODO: temporarily broken - // if self.0.teleported_this_tick { - // self.0.teleported_this_tick = false; - // self.send_packet(dbg!(PlayerPositionAndLook { - // position: self.new_position, - // yaw: self.yaw, - // pitch: self.pitch, - // flags: PlayerPositionAndLookFlags::new(false, false, false, false, false), - // teleport_id: VarInt((self.teleport_id_counter - 1) as i32), - // dismount_vehicle: false, - // })); - // } + if self.0.teleported_this_tick { + self.0.teleported_this_tick = false; + + self.send_packet(PlayerPositionAndLook { + position: self.new_position, + yaw: self.yaw, + pitch: self.pitch, + flags: PlayerPositionAndLookFlags::new(false, false, false, false, false), + teleport_id: VarInt((self.teleport_id_counter - 1) as i32), + dismount_vehicle: false, + }); + } let mut entities_to_unload = Vec::new(); @@ -504,9 +504,7 @@ impl<'a> ClientMut<'a> { // longer visible. self.0.loaded_entities.retain(|&id| { if let Some(entity) = entities.get(id) { - if glm::distance(&self.0.new_position, &entity.position()) - <= view_dist as f64 * 16.0 - { + if self.0.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 { todo!("update entity"); return true; } @@ -524,7 +522,7 @@ impl<'a> ClientMut<'a> { // Spawn new entities within the view distance. for (id, entity) in entities.iter() { - if glm::distance(&self.position(), &entity.position()) <= view_dist as f64 * 16.0 + if self.position().distance(entity.position()) <= view_dist as f64 * 16.0 && entity.typ() != EntityType::Marker && self.0.loaded_entities.insert(id) { @@ -548,7 +546,7 @@ impl<'a> ClientMut<'a> { fn handle_movement_packet( client: &mut Client, - new_position: DVec3, + new_position: Vec3, new_yaw: f32, new_pitch: f32, new_on_ground: bool, @@ -556,8 +554,8 @@ impl<'a> ClientMut<'a> { if client.pending_teleports == 0 { let event = Event::Movement { position: client.new_position, - yaw_degrees: client.yaw, - pitch_degrees: client.pitch, + yaw: client.yaw, + pitch: client.pitch, on_ground: client.on_ground, }; @@ -693,9 +691,9 @@ pub enum Event { /// The client has moved. The values in this variant are the previous /// position and look. Movement { - position: DVec3, - yaw_degrees: f32, - pitch_degrees: f32, + position: Vec3, + yaw: f32, + pitch: f32, on_ground: bool, }, } diff --git a/src/entity.rs b/src/entity.rs index d558314..b4774ed 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -9,18 +9,17 @@ use std::ops::Deref; use bitfield_struct::bitfield; use rayon::iter::ParallelIterator; use uuid::Uuid; +use vek::{Aabb, Vec3}; use crate::byte_angle::ByteAngle; -use crate::chunk::ChunkPos; -use crate::glm::{DVec3, I16Vec3, Vec3}; use crate::packets::play::{ ClientPlayPacket, EntityMetadata, SpawnEntity, SpawnExperienceOrb, SpawnLivingEntity, SpawnPainting, SpawnPlayer, }; use crate::protocol::ReadToEnd; use crate::slotmap::{Key, SlotMap}; +use crate::util::aabb_from_bottom_and_size; use crate::var_int::VarInt; -use crate::{Aabb, WorldId}; pub struct Entities { sm: SlotMap, @@ -103,8 +102,8 @@ impl<'a> EntitiesMut<'a> { let entity = EntityId(self.0.sm.insert(Entity { flags: EntityFlags(0), meta: EntityMeta::new(EntityType::Marker), - new_position: DVec3::default(), - old_position: DVec3::default(), + new_position: Vec3::default(), + old_position: Vec3::default(), yaw: 0.0, pitch: 0.0, head_yaw: 0.0, @@ -181,13 +180,13 @@ impl EntityId { pub struct Entity { flags: EntityFlags, meta: EntityMeta, - new_position: DVec3, - old_position: DVec3, + new_position: Vec3, + old_position: Vec3, yaw: f32, pitch: f32, head_yaw: f32, head_pitch: f32, - velocity: Vec3, + velocity: Vec3, uuid: Uuid, } @@ -230,13 +229,13 @@ impl Entity { } /// Returns the position of this entity in the world it inhabits. - pub fn position(&self) -> DVec3 { + pub fn position(&self) -> Vec3 { self.new_position } /// Returns the position of this entity as it existed at the end of the /// previous tick. - pub fn old_position(&self) -> DVec3 { + pub fn old_position(&self) -> Vec3 { self.old_position } @@ -261,7 +260,7 @@ impl Entity { } /// Gets the velocity of this entity in meters per second. - pub fn velocity(&self) -> Vec3 { + pub fn velocity(&self) -> Vec3 { self.velocity } @@ -369,7 +368,7 @@ impl Entity { } } - pub fn hitbox(&self) -> Aabb { + pub fn hitbox(&self) -> Aabb { let dims = match &self.meta { EntityMeta::AreaEffectCloud(e) => [ e.get_radius() as f64 * 2.0, @@ -504,13 +503,13 @@ impl Entity { EntityMeta::FishingBobber(_) => [0.25, 0.25, 0.25], }; - Aabb::from_bottom_and_dimensions(self.new_position, dims) + aabb_from_bottom_and_size(self.new_position, dims.into()) } } -fn velocity_to_packet_units(vel: Vec3) -> I16Vec3 { +fn velocity_to_packet_units(vel: Vec3) -> Vec3 { // The saturating cast to i16 is desirable. - vel.map(|v| (v * 400.0) as i16) + (vel * 400.0).as_() } impl<'a> EntityMut<'a> { @@ -534,7 +533,7 @@ impl<'a> EntityMut<'a> { } /// Sets the position of this entity in the world it inhabits. - pub fn set_position(&mut self, pos: impl Into) { + pub fn set_position(&mut self, pos: impl Into>) { self.0.new_position = pos.into(); } @@ -570,9 +569,10 @@ impl<'a> EntityMut<'a> { } } - pub fn set_velocity(&mut self, velocity: Vec3) { - self.0.velocity = velocity; - if velocity_to_packet_units(self.velocity) != velocity_to_packet_units(velocity) { + pub fn set_velocity(&mut self, velocity: impl Into>) { + let new_vel = velocity.into(); + self.0.velocity = new_vel; + if velocity_to_packet_units(self.velocity) != velocity_to_packet_units(new_vel) { self.0.flags.set_velocity_modified(true); } } diff --git a/src/lib.rs b/src/lib.rs index ec317b7..af23b74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,6 @@ // missing_docs )] -mod aabb; pub mod block; mod block_pos; mod byte_angle; @@ -27,7 +26,6 @@ mod var_int; mod var_long; pub mod world; -pub use aabb::Aabb; pub use async_trait::async_trait; pub use block_pos::BlockPos; pub use chunk::{Chunk, ChunkPos, Chunks, ChunksMut}; @@ -39,7 +37,7 @@ pub use server::{start_server, NewClientData, Server, ShutdownResult}; pub use text::{Text, TextFormat}; pub use uuid::Uuid; pub use world::{WorldId, WorldMut, WorldRef, Worlds, WorldsMut}; -pub use {nalgebra_glm as glm, nbt, uuid}; +pub use {nbt, uuid, vek}; /// The Minecraft protocol version that this library targets. pub const PROTOCOL_VERSION: i32 = 758; diff --git a/src/packets.rs b/src/packets.rs index d552ced..65d574d 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -13,10 +13,10 @@ use num::{One, Zero}; use paste::paste; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use vek::{Vec2, Vec3}; use crate::block_pos::BlockPos; use crate::byte_angle::ByteAngle; -use crate::glm::{DVec3, I16Vec3, Vec3}; use crate::identifier::Identifier; use crate::protocol::{BoundedArray, BoundedInt, BoundedString, Decode, Encode, Nbt, ReadToEnd}; use crate::var_int::VarInt; @@ -445,18 +445,18 @@ pub mod play { entity_id: VarInt, object_uuid: Uuid, typ: VarInt, - position: DVec3, + position: Vec3, pitch: ByteAngle, yaw: ByteAngle, data: i32, - velocity: I16Vec3, + velocity: Vec3, } } def_struct! { SpawnExperienceOrb 0x01 { entity_id: VarInt, - position: DVec3, + position: Vec3, count: i16, } } @@ -466,11 +466,11 @@ pub mod play { entity_id: VarInt, entity_uuid: Uuid, typ: VarInt, - position: DVec3, + position: Vec3, yaw: ByteAngle, pitch: ByteAngle, head_pitch: ByteAngle, - velocity: I16Vec3, + velocity: Vec3, } } @@ -497,7 +497,7 @@ pub mod play { SpawnPlayer 0x04 { entity_id: VarInt, player_uuid: Uuid, - position: DVec3, + position: Vec3, yaw: ByteAngle, pitch: ByteAngle, } @@ -966,7 +966,7 @@ pub mod play { def_struct! { PlayerPositionAndLook 0x38 { - position: DVec3, + position: Vec3, yaw: f32, pitch: f32, flags: PlayerPositionAndLookFlags, @@ -1290,7 +1290,7 @@ pub mod play { def_struct! { InteractAtData { - target: Vec3, + target: Vec3, hand: Hand, } } @@ -1317,7 +1317,7 @@ pub mod play { def_struct! { PlayerPosition 0x11 { - position: DVec3, + position: Vec3, on_ground: bool, } } @@ -1325,7 +1325,7 @@ pub mod play { def_struct! { PlayerPositionAndRotation 0x12 { // Absolute position - position: DVec3, + position: Vec3, /// Absolute rotation on X axis in degrees. yaw: f32, /// Absolute rotation on Y axis in degrees. @@ -1353,7 +1353,7 @@ pub mod play { def_struct! { VehicleMoveServerbound 0x15 { /// Absolute position - position: DVec3, + position: Vec3, /// Degrees yaw: f32, /// Degrees @@ -1669,7 +1669,7 @@ pub mod play { hand: Hand, location: BlockPos, face: BlockFace, - cursor_pos: Vec3, + cursor_pos: Vec3, head_inside_block: bool, } } diff --git a/src/protocol.rs b/src/protocol.rs index 20d43ee..9d25027 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -5,9 +5,9 @@ use anyhow::{anyhow, ensure, Context}; use arrayvec::ArrayVec; use bitvec::prelude::*; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use nalgebra_glm::{Number, TVec}; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use vek::{Vec2, Vec3, Vec4}; use crate::var_int::VarInt; use crate::EntityId; @@ -372,15 +372,50 @@ impl Decode for [T; N] { } } -impl Encode for TVec { +impl Encode for Vec2 { fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { - encode_array_bounded(self.as_slice(), N, N, w) + self.x.encode(w)?; + self.y.encode(w) } } -impl Decode for TVec { +impl Encode for Vec3 { + fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { + self.x.encode(w)?; + self.y.encode(w)?; + self.z.encode(w) + } +} + +impl Encode for Vec4 { + fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { + self.x.encode(w)?; + self.y.encode(w)?; + self.z.encode(w)?; + self.w.encode(w) + } +} + +impl Decode for Vec2 { fn decode(r: &mut impl Read) -> anyhow::Result { - Ok(<[T; N]>::decode(r)?.into()) + Ok(Vec2::new(T::decode(r)?, T::decode(r)?)) + } +} + +impl Decode for Vec3 { + fn decode(r: &mut impl Read) -> anyhow::Result { + Ok(Vec3::new(T::decode(r)?, T::decode(r)?, T::decode(r)?)) + } +} + +impl Decode for Vec4 { + fn decode(r: &mut impl Read) -> anyhow::Result { + Ok(Vec4::new( + T::decode(r)?, + T::decode(r)?, + T::decode(r)?, + T::decode(r)?, + )) } } diff --git a/src/util.rs b/src/util.rs index 6a21873..de4a5bf 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,9 @@ use std::iter::FusedIterator; +use num::cast::AsPrimitive; +use num::Float; +use vek::{Aabb, Extent3, Vec3}; + use crate::ChunkPos; /// Returns true if the given string meets the criteria for a valid Minecraft @@ -26,3 +30,26 @@ pub fn is_chunk_in_view_distance(center: ChunkPos, other: ChunkPos, distance: u8 (center.x as f64 - other.x as f64).powi(2) + (center.z as f64 - other.z as f64).powi(2) <= (distance as f64 + EXTRA_RADIUS as f64).powi(2) } + +pub(crate) fn aabb_from_bottom_and_size(bottom: Vec3, size: Vec3) -> Aabb +where + T: Float + 'static, + f64: AsPrimitive, +{ + let aabb = Aabb { + min: Vec3::new( + bottom.x - size.x / 2.0.as_(), + bottom.y, + bottom.z - size.z / 2.0.as_(), + ), + max: Vec3::new( + bottom.x + size.x / 2.0.as_(), + bottom.y, + bottom.z + size.z / 2.0.as_(), + ), + }; + + debug_assert!(aabb.is_valid()); + + aabb +}