From 5c0dffbf9dd3d9b55fbc937dd5ddf392f004fe93 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Sun, 1 Sep 2024 18:04:27 +0200 Subject: [PATCH] Dynamic chunk loading --- Cargo.lock | 2 + pumpkin-core/src/lib.rs | 1 + .../src/math}/boundingbox.rs | 5 +- .../math.rs => pumpkin-core/src/math/mod.rs | 11 ++ .../src => pumpkin-core/src/math}/position.rs | 4 +- .../src => pumpkin-core/src/math}/vector2.rs | 0 .../src => pumpkin-core/src/math}/vector3.rs | 0 pumpkin-entity/Cargo.toml | 2 + pumpkin-entity/src/lib.rs | 43 +++++--- pumpkin-protocol/src/bytebuf/packet_id.rs | 22 ++-- .../src/client/play/c_block_destroy_stage.rs | 3 +- .../src/client/play/c_block_update.rs | 3 +- pumpkin-protocol/src/client/play/c_login.rs | 3 +- .../src/client/play/c_worldevent.rs | 3 +- pumpkin-protocol/src/lib.rs | 1 - .../src/server/play/s_player_action.rs | 3 +- .../src/server/play/s_use_item_on.rs | 3 +- pumpkin-world/Cargo.toml | 4 +- pumpkin-world/src/block/mod.rs | 2 +- pumpkin-world/src/chunk.rs | 2 +- pumpkin-world/src/coordinates.rs | 3 +- .../src/cylindrical_chunk_iterator.rs | 83 ++++++++++++++ pumpkin-world/src/level.rs | 2 +- pumpkin-world/src/lib.rs | 4 +- pumpkin-world/src/radial_chunk_iterator.rs | 59 ---------- pumpkin-world/src/world_gen/generator.rs | 2 +- .../src/world_gen/generic_generator.rs | 3 +- pumpkin/src/client/mod.rs | 1 + pumpkin/src/client/player_packet.rs | 66 +++++++----- pumpkin/src/entity/player.rs | 54 ++++++---- pumpkin/src/util/mod.rs | 3 +- pumpkin/src/world/mod.rs | 49 ++++----- pumpkin/src/world/player_chunker.rs | 102 ++++++++++++++++++ 33 files changed, 367 insertions(+), 181 deletions(-) rename {pumpkin/src/util => pumpkin-core/src/math}/boundingbox.rs (89%) rename pumpkin/src/util/math.rs => pumpkin-core/src/math/mod.rs (51%) rename {pumpkin-protocol/src => pumpkin-core/src/math}/position.rs (96%) rename {pumpkin-world/src => pumpkin-core/src/math}/vector2.rs (100%) rename {pumpkin-world/src => pumpkin-core/src/math}/vector3.rs (100%) create mode 100644 pumpkin-world/src/cylindrical_chunk_iterator.rs delete mode 100644 pumpkin-world/src/radial_chunk_iterator.rs create mode 100644 pumpkin/src/world/player_chunker.rs diff --git a/Cargo.lock b/Cargo.lock index 7fa6f7335..d0496d2b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1959,6 +1959,7 @@ version = "0.1.0" dependencies = [ "num-derive", "num-traits", + "pumpkin-core", ] [[package]] @@ -2034,6 +2035,7 @@ dependencies = [ "log", "num-derive", "num-traits", + "pumpkin-core", "rayon", "serde", "serde_json", diff --git a/pumpkin-core/src/lib.rs b/pumpkin-core/src/lib.rs index 9539acae3..f566404df 100644 --- a/pumpkin-core/src/lib.rs +++ b/pumpkin-core/src/lib.rs @@ -1,4 +1,5 @@ pub mod gamemode; +pub mod math; pub mod random; pub mod text; diff --git a/pumpkin/src/util/boundingbox.rs b/pumpkin-core/src/math/boundingbox.rs similarity index 89% rename from pumpkin/src/util/boundingbox.rs rename to pumpkin-core/src/math/boundingbox.rs index cc32ebbfa..800a954a5 100644 --- a/pumpkin/src/util/boundingbox.rs +++ b/pumpkin-core/src/math/boundingbox.rs @@ -1,5 +1,4 @@ -use pumpkin_protocol::position::WorldPosition; -use pumpkin_world::vector3::Vector3; +use super::{position::WorldPosition, vector3::Vector3}; pub struct BoundingBox { pub min_x: f64, @@ -38,6 +37,6 @@ impl BoundingBox { let d = f64::max(f64::max(self.min_x - pos.x, pos.x - self.max_x), 0.0); let e = f64::max(f64::max(self.min_y - pos.y, pos.y - self.max_y), 0.0); let f = f64::max(f64::max(self.min_z - pos.z, pos.z - self.max_z), 0.0); - super::math::squared_magnitude(d, e, f) + super::squared_magnitude(d, e, f) } } diff --git a/pumpkin/src/util/math.rs b/pumpkin-core/src/math/mod.rs similarity index 51% rename from pumpkin/src/util/math.rs rename to pumpkin-core/src/math/mod.rs index ec4ff43d8..14922e321 100644 --- a/pumpkin/src/util/math.rs +++ b/pumpkin-core/src/math/mod.rs @@ -1,3 +1,8 @@ +pub mod boundingbox; +pub mod position; +pub mod vector2; +pub mod vector3; + pub fn wrap_degrees(var: f32) -> f32 { let mut var1 = var % 360.0; if var1 >= 180.0 { @@ -14,3 +19,9 @@ pub fn wrap_degrees(var: f32) -> f32 { pub fn squared_magnitude(a: f64, b: f64, c: f64) -> f64 { a * a + b * b + c * c } + +/// Converts a world coordinate to the corresponding chunk-section coordinate. +// TODO: This proberbly should place not here +pub fn get_section_cord(coord: i32) -> i32 { + coord >> 4 +} diff --git a/pumpkin-protocol/src/position.rs b/pumpkin-core/src/math/position.rs similarity index 96% rename from pumpkin-protocol/src/position.rs rename to pumpkin-core/src/math/position.rs index b9eed35e4..c324e89df 100644 --- a/pumpkin-protocol/src/position.rs +++ b/pumpkin-core/src/math/position.rs @@ -1,6 +1,8 @@ -use pumpkin_world::vector3::Vector3; use serde::{Deserialize, Serialize}; +use super::vector3::Vector3; + +/// Aka Block Position pub struct WorldPosition(pub Vector3); impl Serialize for WorldPosition { diff --git a/pumpkin-world/src/vector2.rs b/pumpkin-core/src/math/vector2.rs similarity index 100% rename from pumpkin-world/src/vector2.rs rename to pumpkin-core/src/math/vector2.rs diff --git a/pumpkin-world/src/vector3.rs b/pumpkin-core/src/math/vector3.rs similarity index 100% rename from pumpkin-world/src/vector3.rs rename to pumpkin-core/src/math/vector3.rs diff --git a/pumpkin-entity/Cargo.toml b/pumpkin-entity/Cargo.toml index 6e73e97cc..2b96f7651 100644 --- a/pumpkin-entity/Cargo.toml +++ b/pumpkin-entity/Cargo.toml @@ -4,5 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] +pumpkin-core = { path = "../pumpkin-core"} + num-traits = "0.2" num-derive = "0.4" \ No newline at end of file diff --git a/pumpkin-entity/src/lib.rs b/pumpkin-entity/src/lib.rs index 0e1c8338a..bf7e91c00 100644 --- a/pumpkin-entity/src/lib.rs +++ b/pumpkin-entity/src/lib.rs @@ -1,5 +1,8 @@ use entity_type::EntityType; use pose::EntityPose; +use pumpkin_core::math::{ + get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, +}; pub mod entity_type; pub mod pose; @@ -9,12 +12,10 @@ pub type EntityId = i32; pub struct Entity { pub entity_id: EntityId, pub entity_type: EntityType, - pub x: f64, - pub y: f64, - pub z: f64, - pub lastx: f64, - pub lasty: f64, - pub lastz: f64, + pub pos: Vector3, + pub block_pos: WorldPosition, + pub chunk_pos: Vector2, + pub yaw: f32, pub head_yaw: f32, pub pitch: f32, @@ -28,12 +29,9 @@ impl Entity { Self { entity_id, entity_type, - x: 0.0, - y: 0.0, - z: 0.0, - lastx: 0.0, - lasty: 0.0, - lastz: 0.0, + pos: Vector3::new(0.0, 0.0, 0.0), + block_pos: WorldPosition(Vector3::new(0, 0, 0)), + chunk_pos: Vector2::new(0, 0), yaw: 0.0, head_yaw: 0.0, pitch: 0.0, @@ -41,4 +39,25 @@ impl Entity { pose: EntityPose::Standing, } } + + pub fn set_pos(&mut self, x: f64, y: f64, z: f64) { + if self.pos.x != x || self.pos.y != y || self.pos.z != z { + self.pos = Vector3::new(x, y, z); + let i = x.floor() as i32; + let j = y.floor() as i32; + let k = z.floor() as i32; + + let block_pos = self.block_pos.0; + if i != block_pos.x || j != block_pos.y || k != block_pos.z { + self.block_pos = WorldPosition(Vector3::new(i, j, k)); + + if get_section_cord(i) != self.chunk_pos.x + || get_section_cord(k) != self.chunk_pos.z + { + self.chunk_pos = + Vector2::new(get_section_cord(block_pos.x), get_section_cord(block_pos.z)); + } + } + } + } } diff --git a/pumpkin-protocol/src/bytebuf/packet_id.rs b/pumpkin-protocol/src/bytebuf/packet_id.rs index 635a8051e..a422e247a 100644 --- a/pumpkin-protocol/src/bytebuf/packet_id.rs +++ b/pumpkin-protocol/src/bytebuf/packet_id.rs @@ -1,3 +1,4 @@ +use bytes::BufMut; use serde::{ de::{self, DeserializeOwned, SeqAccess, Visitor}, Deserialize, Deserializer, Serialize, Serializer, @@ -22,19 +23,16 @@ impl Serialize for VarInt { where S: Serializer, { - let mut val = self.0; - let mut buf: Vec = Vec::new(); - for _ in 0..5 { - let mut b: u8 = val as u8 & 0b01111111; - val >>= 7; - if val != 0 { - b |= 0b10000000; - } - buf.push(b); - if val == 0 { - break; - } + let mut value = self.0 as u32; + let mut buf = Vec::new(); + + while value > 0x7F { + buf.put_u8(value as u8 | 0x80); + value >>= 7; } + + buf.put_u8(value as u8); + serializer.serialize_bytes(&buf) } } diff --git a/pumpkin-protocol/src/client/play/c_block_destroy_stage.rs b/pumpkin-protocol/src/client/play/c_block_destroy_stage.rs index d671f985f..2f57eb42f 100644 --- a/pumpkin-protocol/src/client/play/c_block_destroy_stage.rs +++ b/pumpkin-protocol/src/client/play/c_block_destroy_stage.rs @@ -1,7 +1,8 @@ +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::packet; use serde::Serialize; -use crate::{position::WorldPosition, VarInt}; +use crate::VarInt; #[derive(Serialize)] #[packet(0x06)] diff --git a/pumpkin-protocol/src/client/play/c_block_update.rs b/pumpkin-protocol/src/client/play/c_block_update.rs index d2cd09dd2..8c73b9144 100644 --- a/pumpkin-protocol/src/client/play/c_block_update.rs +++ b/pumpkin-protocol/src/client/play/c_block_update.rs @@ -1,7 +1,8 @@ +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::packet; use serde::Serialize; -use crate::{position::WorldPosition, VarInt}; +use crate::VarInt; #[derive(Serialize)] #[packet(0x09)] diff --git a/pumpkin-protocol/src/client/play/c_login.rs b/pumpkin-protocol/src/client/play/c_login.rs index 0e0b12224..7990bba96 100644 --- a/pumpkin-protocol/src/client/play/c_login.rs +++ b/pumpkin-protocol/src/client/play/c_login.rs @@ -1,7 +1,8 @@ +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::packet; use serde::Serialize; -use crate::{position::WorldPosition, VarInt}; +use crate::VarInt; #[derive(Serialize)] #[packet(0x2B)] diff --git a/pumpkin-protocol/src/client/play/c_worldevent.rs b/pumpkin-protocol/src/client/play/c_worldevent.rs index c9447b71c..a7d479684 100644 --- a/pumpkin-protocol/src/client/play/c_worldevent.rs +++ b/pumpkin-protocol/src/client/play/c_worldevent.rs @@ -1,8 +1,7 @@ +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::packet; use serde::Serialize; -use crate::position::WorldPosition; - #[derive(Serialize)] #[packet(0x28)] pub struct CWorldEvent<'a> { diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index e79388163..dcc139b78 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -8,7 +8,6 @@ pub mod bytebuf; pub mod client; pub mod packet_decoder; pub mod packet_encoder; -pub mod position; pub mod server; pub mod slot; pub mod uuid; diff --git a/pumpkin-protocol/src/server/play/s_player_action.rs b/pumpkin-protocol/src/server/play/s_player_action.rs index 3753ee22a..92f9dcace 100644 --- a/pumpkin-protocol/src/server/play/s_player_action.rs +++ b/pumpkin-protocol/src/server/play/s_player_action.rs @@ -1,7 +1,8 @@ use num_derive::FromPrimitive; +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::packet; -use crate::{position::WorldPosition, VarInt}; +use crate::VarInt; #[derive(serde::Deserialize)] #[packet(0x24)] diff --git a/pumpkin-protocol/src/server/play/s_use_item_on.rs b/pumpkin-protocol/src/server/play/s_use_item_on.rs index b25b761db..fd3c1a02e 100644 --- a/pumpkin-protocol/src/server/play/s_use_item_on.rs +++ b/pumpkin-protocol/src/server/play/s_use_item_on.rs @@ -1,7 +1,8 @@ +use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::packet; use serde::Deserialize; -use crate::{position::WorldPosition, VarInt}; +use crate::VarInt; #[derive(Deserialize)] #[packet(0x38)] diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 7a5d04191..2bc06f7f1 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -4,7 +4,9 @@ version.workspace = true edition.workspace = true [dependencies] -# fastanvil = "0.31" +pumpkin-core = { path = "../pumpkin-core"} + + fastnbt = { git = "https://github.com/owengage/fastnbt.git" } fastsnbt = "0.2" tokio.workspace = true diff --git a/pumpkin-world/src/block/mod.rs b/pumpkin-world/src/block/mod.rs index 59dc8a083..f5fbab664 100644 --- a/pumpkin-world/src/block/mod.rs +++ b/pumpkin-world/src/block/mod.rs @@ -1,10 +1,10 @@ -use crate::vector3::Vector3; use num_derive::FromPrimitive; pub mod block_id; mod block_registry; pub use block_id::BlockId; +use pumpkin_core::math::vector3::Vector3; #[derive(FromPrimitive)] pub enum BlockFace { diff --git a/pumpkin-world/src/chunk.rs b/pumpkin-world/src/chunk.rs index 0ceddf82e..1c13d423c 100644 --- a/pumpkin-world/src/chunk.rs +++ b/pumpkin-world/src/chunk.rs @@ -3,13 +3,13 @@ use std::collections::HashMap; use std::ops::Index; use fastnbt::LongArray; +use pumpkin_core::math::vector2::Vector2; use serde::{Deserialize, Serialize}; use crate::{ block::BlockId, coordinates::{ChunkRelativeBlockCoordinates, Height}, level::{ChunkNotGeneratedError, WorldError}, - vector2::Vector2, WORLD_HEIGHT, }; diff --git a/pumpkin-world/src/coordinates.rs b/pumpkin-world/src/coordinates.rs index b474a1369..fed4a49c4 100644 --- a/pumpkin-world/src/coordinates.rs +++ b/pumpkin-world/src/coordinates.rs @@ -2,9 +2,10 @@ use std::ops::Deref; use derive_more::derive::{AsMut, AsRef, Display, Into}; use num_traits::{PrimInt, Signed, Unsigned}; +use pumpkin_core::math::vector2::Vector2; use serde::{Deserialize, Serialize}; -use crate::{vector2::Vector2, WORLD_LOWEST_Y, WORLD_MAX_Y}; +use crate::{WORLD_LOWEST_Y, WORLD_MAX_Y}; #[derive( Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, AsRef, AsMut, Into, Display, diff --git a/pumpkin-world/src/cylindrical_chunk_iterator.rs b/pumpkin-world/src/cylindrical_chunk_iterator.rs new file mode 100644 index 000000000..24c49c9da --- /dev/null +++ b/pumpkin-world/src/cylindrical_chunk_iterator.rs @@ -0,0 +1,83 @@ +use pumpkin_core::math::vector2::Vector2; + +#[derive(Debug, PartialEq)] +pub struct Cylindrical { + pub center: Vector2, + pub view_distance: i32, +} + +impl Cylindrical { + pub fn new(center: Vector2, view_distance: i32) -> Self { + Self { + center, + view_distance, + } + } + + #[allow(unused_variables)] + pub fn for_each_changed_chunk( + old_cylindrical: Cylindrical, + new_cylindrical: Cylindrical, + mut newly_included: impl FnMut(Vector2), + just_removed: impl FnMut(Vector2), + ignore: bool, + ) { + let min_x = old_cylindrical.get_left().min(new_cylindrical.get_left()); + let max_x = old_cylindrical.get_right().max(new_cylindrical.get_right()); + let min_z = old_cylindrical + .get_bottom() + .min(new_cylindrical.get_bottom()); + let max_z = old_cylindrical.get_top().max(new_cylindrical.get_top()); + + for x in min_x..=max_x { + for z in min_z..=max_z { + // TODO + // let old_is_within = if ignore { + // false + // } else { + // old_cylindrical.is_within_distance(x, z) + // }; + // let new_is_within = if ignore { + // true + // } else { + // new_cylindrical.is_within_distance(x, z) + // }; + + // if old_is_within != new_is_within { + // if new_is_within { + newly_included(Vector2::new(x, z)); + // } else { + // dbg!("aa"); + // just_removed(Vector2::new(x, z)); + // } + // } + } + } + } + + fn get_left(&self) -> i32 { + self.center.x - self.view_distance - 1 + } + + fn get_bottom(&self) -> i32 { + self.center.z - self.view_distance - 1 + } + + fn get_right(&self) -> i32 { + self.center.x + self.view_distance + 1 + } + + fn get_top(&self) -> i32 { + self.center.z + self.view_distance + 1 + } + + #[allow(dead_code)] + fn is_within_distance(&self, x: i32, z: i32) -> bool { + let max_dist_squared = self.view_distance * self.view_distance; + let max_dist = self.view_distance as i64; + let dist_x = (x - self.center.x).abs().max(0) - (1); + let dist_z = (z - self.center.z).abs().max(0) - (1); + let dist_squared = dist_x.pow(2) + (max_dist.min(dist_z as i64) as i32).pow(2); + dist_squared < max_dist_squared + } +} diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 5cece51ab..3b63b58c4 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -8,13 +8,13 @@ use std::{ use flate2::{bufread::ZlibDecoder, read::GzDecoder}; use itertools::Itertools; +use pumpkin_core::math::vector2::Vector2; use rayon::prelude::*; use thiserror::Error; use tokio::sync::mpsc; use crate::{ chunk::ChunkData, - vector2::Vector2, world_gen::{get_world_gen, Seed, WorldGenerator}, }; diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index d39541411..dc1e73d1a 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -2,13 +2,11 @@ pub mod biome; pub mod block; pub mod chunk; pub mod coordinates; +pub mod cylindrical_chunk_iterator; pub mod dimension; pub mod global_registry; pub mod item; pub mod level; -pub mod radial_chunk_iterator; -pub mod vector2; -pub mod vector3; mod world_gen; pub const WORLD_HEIGHT: usize = 384; diff --git a/pumpkin-world/src/radial_chunk_iterator.rs b/pumpkin-world/src/radial_chunk_iterator.rs deleted file mode 100644 index f75acb7df..000000000 --- a/pumpkin-world/src/radial_chunk_iterator.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::vector2::Vector2; - -pub struct RadialIterator { - radius: u32, - direction: usize, - current: Vector2, - step_size: i32, - steps_taken: u32, - steps_in_direction: i32, -} - -impl RadialIterator { - pub fn new(radius: u32) -> Self { - RadialIterator { - radius, - direction: 0, - current: Vector2::new(0, 0), - step_size: 1, - steps_taken: 0, - steps_in_direction: 0, - } - } -} - -impl Iterator for RadialIterator { - type Item = Vector2; - - fn next(&mut self) -> Option { - if self.steps_taken >= self.radius * self.radius * 4 { - return None; - } - - let result = self.current; - - self.steps_in_direction += 1; - - // Move in the current direction - match self.direction { - 0 => self.current.x += 1, // East - 1 => self.current.z += 1, // North - 2 => self.current.x -= 1, // West - 3 => self.current.z -= 1, // South - _ => {} - } - - if self.steps_in_direction >= self.step_size { - self.direction = (self.direction + 1) % 4; - self.steps_in_direction = 0; - - // Increase step size after completing two directions - if self.direction == 0 || self.direction == 2 { - self.step_size += 1; - } - } - - self.steps_taken += 1; - Some(result) - } -} diff --git a/pumpkin-world/src/world_gen/generator.rs b/pumpkin-world/src/world_gen/generator.rs index 14e2a5aa2..75ec90838 100644 --- a/pumpkin-world/src/world_gen/generator.rs +++ b/pumpkin-world/src/world_gen/generator.rs @@ -1,10 +1,10 @@ +use pumpkin_core::math::vector2::Vector2; use static_assertions::assert_obj_safe; use crate::biome::Biome; use crate::block::BlockId; use crate::chunk::ChunkData; use crate::coordinates::{BlockCoordinates, XZBlockCoordinates}; -use crate::vector2::Vector2; use crate::world_gen::Seed; pub trait GeneratorInit { diff --git a/pumpkin-world/src/world_gen/generic_generator.rs b/pumpkin-world/src/world_gen/generic_generator.rs index 0f71589c6..6cb87ce22 100644 --- a/pumpkin-world/src/world_gen/generic_generator.rs +++ b/pumpkin-world/src/world_gen/generic_generator.rs @@ -1,7 +1,8 @@ +use pumpkin_core::math::vector2::Vector2; + use crate::{ chunk::{ChunkBlocks, ChunkData}, coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates}, - vector2::Vector2, WORLD_LOWEST_Y, WORLD_MAX_Y, }; diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 913019b90..131e12df9 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -34,6 +34,7 @@ mod client_packet; mod container; pub mod player_packet; +#[derive(Clone)] pub struct PlayerConfig { pub locale: String, // 16 pub view_distance: i8, diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 1602a5d89..45fb667a5 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -4,11 +4,15 @@ use crate::{ commands::{handle_command, CommandSender}, entity::player::{ChatMode, Hand, Player}, server::Server, - util::math::wrap_degrees, + world::player_chunker, }; use num_traits::FromPrimitive; use pumpkin_config::ADVANCED_CONFIG; -use pumpkin_core::{text::TextComponent, GameMode}; +use pumpkin_core::{ + math::{position::WorldPosition, wrap_degrees}, + text::TextComponent, + GameMode, +}; use pumpkin_entity::EntityId; use pumpkin_inventory::WindowType; use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem}; @@ -18,7 +22,6 @@ use pumpkin_protocol::{ CHeadRot, CHurtAnimation, CPingResponse, CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, CWorldEvent, FilterType, }, - position::WorldPosition, server::play::{ Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, @@ -46,9 +49,7 @@ impl Player { if let Some((id, position)) = self.awaiting_teleport.as_ref() { if id == &confirm_teleport.teleport_id { // we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported - self.entity.x = position.x; - self.entity.y = position.y; - self.entity.z = position.z; + self.entity.set_pos(position.x, position.y, position.z); self.awaiting_teleport = None; } else { @@ -75,21 +76,24 @@ impl Player { return; } let entity = &mut self.entity; - entity.lastx = entity.x; - entity.lasty = entity.y; - entity.lastz = entity.z; - entity.x = Self::clamp_horizontal(position.x); - entity.y = Self::clamp_vertical(position.feet_y); - entity.z = Self::clamp_horizontal(position.z); + self.lastx = entity.pos.x; + self.lasty = entity.pos.y; + self.lastz = entity.pos.z; + entity.set_pos( + Self::clamp_horizontal(position.x), + Self::clamp_vertical(position.feet_y), + Self::clamp_horizontal(position.z), + ); // TODO: teleport when moving > 8 block // send new position to all other players let on_ground = self.on_ground; let entity_id = entity.entity_id; - let (x, lastx) = (entity.x, entity.lastx); - let (y, lasty) = (entity.y, entity.lasty); - let (z, lastz) = (entity.z, entity.lastz); - let world = self.world.lock().await; + let (x, lastx) = (entity.pos.x, self.lastx); + let (y, lasty) = (entity.pos.y, self.lasty); + let (z, lastz) = (entity.pos.z, self.lastz); + let world = self.world.clone(); + let world = world.lock().await; world.broadcast_packet( &[&self.client.token], &CUpdateEntityPos::new( @@ -100,6 +104,7 @@ impl Player { on_ground, ), ); + player_chunker::update_position(&world, self).await; } pub async fn handle_position_rotation( @@ -120,25 +125,28 @@ impl Player { } let entity = &mut self.entity; - entity.lastx = entity.x; - entity.lasty = entity.y; - entity.lastz = entity.z; - entity.x = Self::clamp_horizontal(position_rotation.x); - entity.y = Self::clamp_vertical(position_rotation.feet_y); - entity.z = Self::clamp_horizontal(position_rotation.z); + self.lastx = entity.pos.x; + self.lasty = entity.pos.y; + self.lastz = entity.pos.z; + entity.set_pos( + Self::clamp_horizontal(position_rotation.x), + Self::clamp_vertical(position_rotation.feet_y), + Self::clamp_horizontal(position_rotation.z), + ); entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0; entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0; // send new position to all other players let on_ground = self.on_ground; let entity_id = entity.entity_id; - let (x, lastx) = (entity.x, entity.lastx); - let (y, lasty) = (entity.y, entity.lasty); - let (z, lastz) = (entity.z, entity.lastz); + let (x, lastx) = (entity.pos.x, self.lastx); + let (y, lasty) = (entity.pos.y, self.lasty); + let (z, lastz) = (entity.pos.z, self.lastz); let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0); let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); - let world = self.world.lock().await; + let world = self.world.clone(); + let world = world.lock().await; world.broadcast_packet( &[&self.client.token], @@ -156,6 +164,8 @@ impl Player { &[&self.client.token], &CHeadRot::new(entity_id.into(), yaw as u8), ); + + player_chunker::update_position(&world, self).await; } pub async fn handle_rotation(&mut self, _server: &mut Server, rotation: SPlayerRotation) { @@ -299,7 +309,7 @@ impl Player { Hand::from_i32(client_information.main_hand.into()), ChatMode::from_i32(client_information.chat_mode.into()), ) { - self.client.config = Some(PlayerConfig { + self.config = PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, chat_mode, @@ -308,7 +318,7 @@ impl Player { main_hand, text_filtering: client_information.text_filtering, server_listing: client_information.server_listing, - }); + }; } else { self.kick(TextComponent::text("Invalid hand or chat type")) } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 1dd05cb50..d5688a5d6 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -2,7 +2,11 @@ use std::sync::Arc; use num_derive::FromPrimitive; use num_traits::ToPrimitive; -use pumpkin_core::{text::TextComponent, GameMode}; +use pumpkin_core::{ + math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3}, + text::TextComponent, + GameMode, +}; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, Entity, EntityId}; use pumpkin_inventory::player::PlayerInventory; use pumpkin_protocol::{ @@ -11,7 +15,6 @@ use pumpkin_protocol::{ CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetEntityMetadata, CSyncPlayerPosition, CSystemChatMessage, Metadata, PlayerAction, }, - position::WorldPosition, server::play::{ SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, @@ -20,12 +23,10 @@ use pumpkin_protocol::{ }, ConnectionState, RawPacket, ServerPacket, VarInt, }; -use pumpkin_world::vector3::Vector3; use crate::{ - client::{authentication::GameProfile, Client}, + client::{authentication::GameProfile, Client, PlayerConfig}, server::Server, - util::boundingbox::BoundingBox, world::World, }; @@ -55,6 +56,7 @@ pub struct Player { pub gameprofile: GameProfile, pub client: Client, pub entity: Entity, + pub config: PlayerConfig, // TODO: Put this into entity pub world: Arc>, /// Current gamemode @@ -67,6 +69,9 @@ pub struct Player { /// send `send_abilties_update` when changed pub abilities: PlayerAbilities, + pub lastx: f64, + pub lasty: f64, + pub lastz: f64, // Client side value, Should be not trusted pub on_ground: bool, @@ -82,6 +87,8 @@ pub struct Player { pub teleport_id_count: i32, // Current awaiting teleport id and location, None if did not teleport pub awaiting_teleport: Option<(VarInt, Vector3)>, + + pub watched_section: Vector3, } impl Player { @@ -103,8 +110,9 @@ impl Player { } } }; - + let config = client.config.clone().unwrap_or_default(); Self { + config, gameprofile, client, entity: Entity::new(entity_id, EntityType::Player, 1.62), @@ -123,6 +131,10 @@ impl Player { teleport_id_count: 0, abilities: PlayerAbilities::default(), gamemode, + watched_section: Vector3::new(0, 0, 0), + lastx: 0.0, + lasty: 0.0, + lastz: 0.0, } } @@ -185,11 +197,11 @@ impl Player { assert!(self.sneaking != sneaking); self.sneaking = sneaking; self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; - if sneaking { - self.set_pose(EntityPose::Crouching).await; - } else { - self.set_pose(EntityPose::Standing).await; - } + // if sneaking { + // self.set_pose(EntityPose::Crouching).await; + // } else { + // self.set_pose(EntityPose::Standing).await; + // } } pub async fn set_sprinting(&mut self, sprinting: bool) { @@ -242,12 +254,10 @@ impl Player { self.teleport_id_count = 0; } let entity = &mut self.entity; - entity.x = x; - entity.y = y; - entity.z = z; - entity.lastx = x; - entity.lasty = y; - entity.lastz = z; + entity.set_pos(x, y, z); + self.lastx = x; + self.lasty = y; + self.lastz = z; entity.yaw = yaw; entity.pitch = pitch; self.awaiting_teleport = Some((self.teleport_id_count.into(), Vector3::new(x, y, z))); @@ -274,9 +284,9 @@ impl Player { let d = self.block_interaction_range() + additional_range; let box_pos = BoundingBox::from_block(pos); box_pos.squared_magnitude(Vector3 { - x: self.entity.x, - y: self.entity.y + self.entity.standing_eye_height as f64, - z: self.entity.z, + x: self.entity.pos.x, + y: self.entity.pos.y + self.entity.standing_eye_height as f64, + z: self.entity.pos.z, }) < d * d } @@ -441,13 +451,13 @@ impl Player { } } -#[derive(FromPrimitive)] +#[derive(FromPrimitive, Clone)] pub enum Hand { Main, Off, } -#[derive(FromPrimitive)] +#[derive(FromPrimitive, Clone)] pub enum ChatMode { Enabled, CommandsOnly, diff --git a/pumpkin/src/util/mod.rs b/pumpkin/src/util/mod.rs index 59887f233..8b1378917 100644 --- a/pumpkin/src/util/mod.rs +++ b/pumpkin/src/util/mod.rs @@ -1,2 +1 @@ -pub mod boundingbox; -pub mod math; + diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 77123a0ab..0672e049f 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -4,23 +4,25 @@ use std::{ sync::{Arc, Mutex, MutexGuard}, }; +pub mod player_chunker; + use mio::Token; use num_traits::ToPrimitive; use pumpkin_config::BasicConfiguration; +use pumpkin_core::math::vector2::Vector2; use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_protocol::{ client::play::{ - CCenterChunk, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, - CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, - PlayerAction, + CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities, + CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, PlayerAction, }, uuid::UUID, ClientPacket, VarInt, }; -use pumpkin_world::{level::Level, radial_chunk_iterator::RadialIterator}; +use pumpkin_world::level::Level; use tokio::sync::mpsc; -use crate::entity::player::Player; +use crate::{client::Client, entity::player::Player}; pub struct World { pub level: Arc>, @@ -90,7 +92,7 @@ impl World { // TODO: this is for debug purpose, remove later player .client - .send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1)); + .send_packet(&CPlayerAbilities::new(0x02, 0.4, 0.1)); // teleport let x = 10.0; @@ -191,12 +193,12 @@ impl World { existing_player.entity_id().into(), UUID(gameprofile.id), (EntityType::Player as i32).into(), - entity.x, - entity.y, - entity.z, + entity.pos.x, + entity.pos.y, + entity.pos.z, entity.yaw, entity.pitch, - entity.pitch, + entity.head_yaw, 0.into(), 0.0, 0.0, @@ -214,26 +216,28 @@ impl World { self.broadcast_packet(&[&player.client.token], &packet) } - self.spawn_test_chunk(player, base_config.view_distance as u32) - .await; + // Spawn in inital chunks + player_chunker::player_join(self, player).await; } - async fn spawn_test_chunk(&self, player: &mut Player, distance: u32) { + async fn spawn_world_chunks( + &self, + client: &mut Client, + chunks: Vec>, + distance: i32, + ) { let inst = std::time::Instant::now(); let (sender, mut chunk_receiver) = mpsc::channel(distance as usize); - let chunks: Vec<_> = RadialIterator::new(distance).collect(); let level = self.level.clone(); tokio::spawn(async move { level.lock().unwrap().fetch_chunks(&chunks, sender); }); - player.client.send_packet(&CCenterChunk { - chunk_x: 0.into(), - chunk_z: 0.into(), - }); - while let Some(chunk_data) = chunk_receiver.recv().await { + if client.closed { + return; + } // dbg!(chunk_pos); let chunk_data = match chunk_data { Ok(d) => d, @@ -252,10 +256,9 @@ impl World { len / (1024 * 1024) ); } - player.client.send_packet(&CChunkData(&chunk_data)); + client.send_packet(&CChunkData(&chunk_data)); } - let t = inst.elapsed(); - dbg!("DONE", t); + dbg!("DONE CHUNKS", inst.elapsed()); } /// TODO: This definitly should be in world @@ -283,12 +286,10 @@ impl World { // todo: put this into the entitiy struct let id = player.entity_id(); let uuid = player.gameprofile.id; - dbg!("1"); self.broadcast_packet( &[&player.client.token], &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), ); - dbg!("2"); self.broadcast_packet(&[&player.client.token], &CRemoveEntities::new(&[id.into()])) } } diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs new file mode 100644 index 000000000..a060dfbd0 --- /dev/null +++ b/pumpkin/src/world/player_chunker.rs @@ -0,0 +1,102 @@ +use pumpkin_config::BASIC_CONFIG; +use pumpkin_core::math::{ + get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, +}; +use pumpkin_protocol::client::play::{CCenterChunk, CUnloadChunk}; +use pumpkin_world::cylindrical_chunk_iterator::Cylindrical; + +use crate::entity::player::Player; + +use super::World; + +fn get_view_distance(player: &Player) -> i8 { + player + .config + .view_distance + .clamp(2, BASIC_CONFIG.view_distance as i8) +} + +pub async fn player_join(world: &World, player: &mut Player) { + let new_watched = chunk_section_from_pos(&player.entity.block_pos); + player.watched_section = new_watched; + let chunk_pos = player.entity.chunk_pos; + player.client.send_packet(&CCenterChunk { + chunk_x: chunk_pos.x.into(), + chunk_z: chunk_pos.z.into(), + }); + let view_distance = get_view_distance(player) as i32; + dbg!(view_distance); + let old_cylindrical = Cylindrical::new( + Vector2::new(player.watched_section.x, player.watched_section.z), + view_distance, + ); + let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance); + let mut loading_chunks = Vec::new(); + Cylindrical::for_each_changed_chunk( + old_cylindrical, + new_cylindrical, + |chunk_pos| { + loading_chunks.push(chunk_pos); + }, + |chunk_pos| { + player + .client + .send_packet(&CUnloadChunk::new(chunk_pos.x, chunk_pos.z)); + }, + true, + ); + if !loading_chunks.is_empty() { + world + .spawn_world_chunks(&mut player.client, loading_chunks, view_distance) + .await; + } +} + +pub async fn update_position(world: &World, player: &mut Player) { + let current_watched = player.watched_section; + let new_watched = chunk_section_from_pos(&player.entity.block_pos); + if current_watched != new_watched { + let chunk_pos = player.entity.chunk_pos; + player.client.send_packet(&CCenterChunk { + chunk_x: chunk_pos.x.into(), + chunk_z: chunk_pos.z.into(), + }); + + let view_distance = get_view_distance(player) as i32; + let old_cylindrical = Cylindrical::new( + Vector2::new(player.watched_section.x, player.watched_section.z), + view_distance, + ); + let new_cylindrical = + Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance); + player.watched_section = new_watched; + let mut loading_chunks = Vec::new(); + Cylindrical::for_each_changed_chunk( + old_cylindrical, + new_cylindrical, + |chunk_pos| { + loading_chunks.push(chunk_pos); + }, + |chunk_pos| { + player + .client + .send_packet(&CUnloadChunk::new(chunk_pos.x, chunk_pos.z)); + }, + false, + ); + if !loading_chunks.is_empty() { + world + .spawn_world_chunks(&mut player.client, loading_chunks, view_distance) + .await; + } + } +} + +fn chunk_section_from_pos(block_pos: &WorldPosition) -> Vector3 { + let block_pos = block_pos.0; + Vector3::new( + get_section_cord(block_pos.x), + get_section_cord(block_pos.y), + get_section_cord(block_pos.z), + ) +}