From 452d8c413b0e54a1b29eeda73fd0fdbddddebaf2 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sat, 31 Jan 2026 15:24:11 +0100 Subject: [PATCH] chore: add uuid to world --- pumpkin-protocol/src/codec/velocity.rs | 89 ++++++++++++++++++- .../src/java/client/play/entity_velocity.rs | 4 +- .../src/java/client/play/login.rs | 12 +-- pumpkin/src/world/mod.rs | 13 ++- 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/pumpkin-protocol/src/codec/velocity.rs b/pumpkin-protocol/src/codec/velocity.rs index a7160094b..856227291 100644 --- a/pumpkin-protocol/src/codec/velocity.rs +++ b/pumpkin-protocol/src/codec/velocity.rs @@ -1,10 +1,16 @@ +use std::fmt::Formatter; + use pumpkin_util::math::vector3::Vector3; use crate::{ VarInt, - ser::{NetworkWriteExt, WritingError}, + ser::{NetworkWriteExt, ReadingError, WritingError}, +}; +use serde::{ + Deserializer, Serialize, + de::{self, Visitor}, + ser::Serializer, }; -use serde::{Serialize, ser::Serializer}; #[derive(Clone, Copy)] pub struct Velocity(pub Vector3); @@ -58,6 +64,52 @@ impl Velocity { Ok(()) } + + pub fn read(reader: &mut R) -> Result { + let mut low_16 = [0u8; 2]; + reader + .read_exact(&mut low_16) + .map_err(|e| ReadingError::Message(e.to_string()))?; + + if low_16[0] == 0 && low_16[1] == 0 { + return Ok(Self(Vector3::new(0.0, 0.0, 0.0))); + } + + let mut mid_32 = [0u8; 4]; + reader + .read_exact(&mut mid_32) + .map_err(|e| ReadingError::Message(e.to_string()))?; + + let low = u16::from_le_bytes(low_16) as i64; + let mid = i32::from_be_bytes(mid_32) as i64; + let packed_data = low | (mid << 16); + + let header = packed_data & 0x07; + let is_extended = (header & 4) != 0; + + let scale_factor = if is_extended { + let scale_tail = VarInt::decode(reader)?; + ((scale_tail.0 as i64) << 2) | (header & 3) + } else { + header & 3 + }; + + if scale_factor == 0 && !is_extended { + return Ok(Self(Vector3::new(0.0, 0.0, 0.0))); + } + + let q_x = (packed_data >> 3) & 0x7FFF; + let q_y = (packed_data >> 18) & 0x7FFF; + let q_z = (packed_data >> 33) & 0x7FFF; + + let scale = scale_factor as f64; + + Ok(Self(Vector3::new( + from_long(q_x, scale), + from_long(q_y, scale), + from_long(q_z, scale), + ))) + } } const MAX_VELOCITY_CLAMP: f64 = 1.717_986_918_3E10; @@ -79,6 +131,12 @@ fn to_long(value: f64) -> i64 { ((value.mul_add(0.5, 0.5) * MAX_15_BIT_VALUE).round() as i64).clamp(0, 32766) } +fn from_long(quantized: i64, scale: f64) -> f64 { + // Reverse: ((v * 0.5 + 0.5) * 32766) -> v + let normalized = (quantized as f64 / MAX_15_BIT_VALUE) - 0.5; + (normalized / 0.5) * scale +} + impl Serialize for Velocity { fn serialize(&self, serializer: S) -> Result { let mut buf = Vec::new(); @@ -86,3 +144,30 @@ impl Serialize for Velocity { serializer.serialize_bytes(&buf) } } + +impl<'de> de::Deserialize<'de> for Velocity { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct VelocityVisitor; + + impl Visitor<'_> for VelocityVisitor { + type Value = Velocity; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("a byte array representing bit-packed velocity") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + let mut cursor = std::io::Cursor::new(v); + Velocity::read(&mut cursor).map_err(de::Error::custom) + } + } + + deserializer.deserialize_bytes(VelocityVisitor) + } +} diff --git a/pumpkin-protocol/src/java/client/play/entity_velocity.rs b/pumpkin-protocol/src/java/client/play/entity_velocity.rs index a3d175645..393161762 100644 --- a/pumpkin-protocol/src/java/client/play/entity_velocity.rs +++ b/pumpkin-protocol/src/java/client/play/entity_velocity.rs @@ -1,7 +1,7 @@ use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_MOTION; use pumpkin_macros::java_packet; use pumpkin_util::math::vector3::Vector3; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::{VarInt, codec::velocity::Velocity}; @@ -9,7 +9,7 @@ use crate::{VarInt, codec::velocity::Velocity}; /// /// This packet informs the client of a sudden change in an entity's movement, /// such as knockback from an attack, explosions, or being launched by a piston. -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] #[java_packet(PLAY_SET_ENTITY_MOTION)] pub struct CEntityVelocity { /// The Entity ID of the entity whose velocity is being set diff --git a/pumpkin-protocol/src/java/client/play/login.rs b/pumpkin-protocol/src/java/client/play/login.rs index 0b4a3d4cf..0113f47fe 100644 --- a/pumpkin-protocol/src/java/client/play/login.rs +++ b/pumpkin-protocol/src/java/client/play/login.rs @@ -2,7 +2,7 @@ use pumpkin_data::packet::clientbound::PLAY_LOGIN; use pumpkin_util::{math::position::BlockPos, resource_location::ResourceLocation}; use pumpkin_macros::java_packet; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::VarInt; @@ -12,14 +12,14 @@ use crate::VarInt; /// This is one of the largest and most important packets in the protocol. It /// initializes the player's world view, dimension settings, and local game /// rules. Once received, the client begins rendering the world. -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] #[java_packet(PLAY_LOGIN)] -pub struct CLogin<'a> { +pub struct CLogin { /// The unique ID assigned to the player for the current session. pub entity_id: i32, pub is_hardcore: bool, /// A list of all dimensions present on the server (e.g., overworld, nether, end). - pub dimension_names: &'a [ResourceLocation], + pub dimension_names: Vec, pub max_players: VarInt, /// The number of chunks the client will render in each direction. pub view_distance: VarInt, @@ -52,14 +52,14 @@ pub struct CLogin<'a> { pub enforce_secure_chat: bool, } -impl<'a> CLogin<'a> { +impl CLogin { #[expect(clippy::too_many_arguments)] #[expect(clippy::fn_params_excessive_bools)] #[must_use] pub const fn new( entity_id: i32, is_hardcore: bool, - dimension_names: &'a [ResourceLocation], + dimension_names: Vec, max_players: VarInt, view_distance: VarInt, simulated_distance: VarInt, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index dc8c3557b..678aac6f7 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -165,6 +165,8 @@ impl PumpkinError for GetBlockError { /// - Stores and tracks active `Player` entities within the world. /// - Provides a central hub for interacting with the world's entities and environment. pub struct World { + /// Represents the World's Unique Identifier + pub uuid: Uuid, /// The underlying level, responsible for chunk management and terrain generation. pub level: Arc, pub level_info: Arc>, @@ -197,6 +199,14 @@ pub struct World { pub portal_poi: Mutex, } +impl PartialEq for World { + fn eq(&self, other: &Self) -> bool { + self.uuid == other.uuid + } +} + +impl Eq for World {} + impl World { #[must_use] pub fn load( @@ -213,6 +223,7 @@ impl World { let portal_poi = portal::PortalPoiStorage::new(&level.level_folder.root_folder); Self { + uuid: Uuid::new_v4(), level, level_info, players: ArcSwap::new(Arc::new(Vec::new())), @@ -1445,7 +1456,7 @@ impl World { .send_packet_now(&CLogin::new( entity_id, base_config.hardcore, - &dimensions, + dimensions, base_config.max_players.try_into().unwrap(), base_config.view_distance.get().into(), // TODO: view distance base_config.simulation_distance.get().into(), // TODO: sim view dinstance