Dynamic chunk loading

This commit is contained in:
Snowiiii 2024-09-01 18:04:27 +02:00
parent 2085307a44
commit 5c0dffbf9d
33 changed files with 367 additions and 181 deletions

2
Cargo.lock generated
View File

@ -1959,6 +1959,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"num-derive", "num-derive",
"num-traits", "num-traits",
"pumpkin-core",
] ]
[[package]] [[package]]
@ -2034,6 +2035,7 @@ dependencies = [
"log", "log",
"num-derive", "num-derive",
"num-traits", "num-traits",
"pumpkin-core",
"rayon", "rayon",
"serde", "serde",
"serde_json", "serde_json",

View File

@ -1,4 +1,5 @@
pub mod gamemode; pub mod gamemode;
pub mod math;
pub mod random; pub mod random;
pub mod text; pub mod text;

View File

@ -1,5 +1,4 @@
use pumpkin_protocol::position::WorldPosition; use super::{position::WorldPosition, vector3::Vector3};
use pumpkin_world::vector3::Vector3;
pub struct BoundingBox { pub struct BoundingBox {
pub min_x: f64, 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 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 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); 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)
} }
} }

View File

@ -1,3 +1,8 @@
pub mod boundingbox;
pub mod position;
pub mod vector2;
pub mod vector3;
pub fn wrap_degrees(var: f32) -> f32 { pub fn wrap_degrees(var: f32) -> f32 {
let mut var1 = var % 360.0; let mut var1 = var % 360.0;
if var1 >= 180.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 { pub fn squared_magnitude(a: f64, b: f64, c: f64) -> f64 {
a * a + b * b + c * c 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
}

View File

@ -1,6 +1,8 @@
use pumpkin_world::vector3::Vector3;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::vector3::Vector3;
/// Aka Block Position
pub struct WorldPosition(pub Vector3<i32>); pub struct WorldPosition(pub Vector3<i32>);
impl Serialize for WorldPosition { impl Serialize for WorldPosition {

View File

@ -4,5 +4,7 @@ version.workspace = true
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
pumpkin-core = { path = "../pumpkin-core"}
num-traits = "0.2" num-traits = "0.2"
num-derive = "0.4" num-derive = "0.4"

View File

@ -1,5 +1,8 @@
use entity_type::EntityType; use entity_type::EntityType;
use pose::EntityPose; use pose::EntityPose;
use pumpkin_core::math::{
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
};
pub mod entity_type; pub mod entity_type;
pub mod pose; pub mod pose;
@ -9,12 +12,10 @@ pub type EntityId = i32;
pub struct Entity { pub struct Entity {
pub entity_id: EntityId, pub entity_id: EntityId,
pub entity_type: EntityType, pub entity_type: EntityType,
pub x: f64, pub pos: Vector3<f64>,
pub y: f64, pub block_pos: WorldPosition,
pub z: f64, pub chunk_pos: Vector2<i32>,
pub lastx: f64,
pub lasty: f64,
pub lastz: f64,
pub yaw: f32, pub yaw: f32,
pub head_yaw: f32, pub head_yaw: f32,
pub pitch: f32, pub pitch: f32,
@ -28,12 +29,9 @@ impl Entity {
Self { Self {
entity_id, entity_id,
entity_type, entity_type,
x: 0.0, pos: Vector3::new(0.0, 0.0, 0.0),
y: 0.0, block_pos: WorldPosition(Vector3::new(0, 0, 0)),
z: 0.0, chunk_pos: Vector2::new(0, 0),
lastx: 0.0,
lasty: 0.0,
lastz: 0.0,
yaw: 0.0, yaw: 0.0,
head_yaw: 0.0, head_yaw: 0.0,
pitch: 0.0, pitch: 0.0,
@ -41,4 +39,25 @@ impl Entity {
pose: EntityPose::Standing, 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));
}
}
}
}
} }

View File

@ -1,3 +1,4 @@
use bytes::BufMut;
use serde::{ use serde::{
de::{self, DeserializeOwned, SeqAccess, Visitor}, de::{self, DeserializeOwned, SeqAccess, Visitor},
Deserialize, Deserializer, Serialize, Serializer, Deserialize, Deserializer, Serialize, Serializer,
@ -22,19 +23,16 @@ impl Serialize for VarInt {
where where
S: Serializer, S: Serializer,
{ {
let mut val = self.0; let mut value = self.0 as u32;
let mut buf: Vec<u8> = Vec::new(); let mut buf = Vec::new();
for _ in 0..5 {
let mut b: u8 = val as u8 & 0b01111111; while value > 0x7F {
val >>= 7; buf.put_u8(value as u8 | 0x80);
if val != 0 { value >>= 7;
b |= 0b10000000;
}
buf.push(b);
if val == 0 {
break;
}
} }
buf.put_u8(value as u8);
serializer.serialize_bytes(&buf) serializer.serialize_bytes(&buf)
} }
} }

View File

@ -1,7 +1,8 @@
use pumpkin_core::math::position::WorldPosition;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::{position::WorldPosition, VarInt}; use crate::VarInt;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x06)] #[packet(0x06)]

View File

@ -1,7 +1,8 @@
use pumpkin_core::math::position::WorldPosition;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::{position::WorldPosition, VarInt}; use crate::VarInt;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x09)] #[packet(0x09)]

View File

@ -1,7 +1,8 @@
use pumpkin_core::math::position::WorldPosition;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::{position::WorldPosition, VarInt}; use crate::VarInt;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x2B)] #[packet(0x2B)]

View File

@ -1,8 +1,7 @@
use pumpkin_core::math::position::WorldPosition;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::position::WorldPosition;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x28)] #[packet(0x28)]
pub struct CWorldEvent<'a> { pub struct CWorldEvent<'a> {

View File

@ -8,7 +8,6 @@ pub mod bytebuf;
pub mod client; pub mod client;
pub mod packet_decoder; pub mod packet_decoder;
pub mod packet_encoder; pub mod packet_encoder;
pub mod position;
pub mod server; pub mod server;
pub mod slot; pub mod slot;
pub mod uuid; pub mod uuid;

View File

@ -1,7 +1,8 @@
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use pumpkin_core::math::position::WorldPosition;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use crate::{position::WorldPosition, VarInt}; use crate::VarInt;
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
#[packet(0x24)] #[packet(0x24)]

View File

@ -1,7 +1,8 @@
use pumpkin_core::math::position::WorldPosition;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Deserialize; use serde::Deserialize;
use crate::{position::WorldPosition, VarInt}; use crate::VarInt;
#[derive(Deserialize)] #[derive(Deserialize)]
#[packet(0x38)] #[packet(0x38)]

View File

@ -4,7 +4,9 @@ version.workspace = true
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
# fastanvil = "0.31" pumpkin-core = { path = "../pumpkin-core"}
fastnbt = { git = "https://github.com/owengage/fastnbt.git" } fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2" fastsnbt = "0.2"
tokio.workspace = true tokio.workspace = true

View File

@ -1,10 +1,10 @@
use crate::vector3::Vector3;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
pub mod block_id; pub mod block_id;
mod block_registry; mod block_registry;
pub use block_id::BlockId; pub use block_id::BlockId;
use pumpkin_core::math::vector3::Vector3;
#[derive(FromPrimitive)] #[derive(FromPrimitive)]
pub enum BlockFace { pub enum BlockFace {

View File

@ -3,13 +3,13 @@ use std::collections::HashMap;
use std::ops::Index; use std::ops::Index;
use fastnbt::LongArray; use fastnbt::LongArray;
use pumpkin_core::math::vector2::Vector2;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
block::BlockId, block::BlockId,
coordinates::{ChunkRelativeBlockCoordinates, Height}, coordinates::{ChunkRelativeBlockCoordinates, Height},
level::{ChunkNotGeneratedError, WorldError}, level::{ChunkNotGeneratedError, WorldError},
vector2::Vector2,
WORLD_HEIGHT, WORLD_HEIGHT,
}; };

View File

@ -2,9 +2,10 @@ use std::ops::Deref;
use derive_more::derive::{AsMut, AsRef, Display, Into}; use derive_more::derive::{AsMut, AsRef, Display, Into};
use num_traits::{PrimInt, Signed, Unsigned}; use num_traits::{PrimInt, Signed, Unsigned};
use pumpkin_core::math::vector2::Vector2;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{vector2::Vector2, WORLD_LOWEST_Y, WORLD_MAX_Y}; use crate::{WORLD_LOWEST_Y, WORLD_MAX_Y};
#[derive( #[derive(
Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, AsRef, AsMut, Into, Display, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, AsRef, AsMut, Into, Display,

View File

@ -0,0 +1,83 @@
use pumpkin_core::math::vector2::Vector2;
#[derive(Debug, PartialEq)]
pub struct Cylindrical {
pub center: Vector2<i32>,
pub view_distance: i32,
}
impl Cylindrical {
pub fn new(center: Vector2<i32>, 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<i32>),
just_removed: impl FnMut(Vector2<i32>),
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
}
}

View File

@ -8,13 +8,13 @@ use std::{
use flate2::{bufread::ZlibDecoder, read::GzDecoder}; use flate2::{bufread::ZlibDecoder, read::GzDecoder};
use itertools::Itertools; use itertools::Itertools;
use pumpkin_core::math::vector2::Vector2;
use rayon::prelude::*; use rayon::prelude::*;
use thiserror::Error; use thiserror::Error;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::{ use crate::{
chunk::ChunkData, chunk::ChunkData,
vector2::Vector2,
world_gen::{get_world_gen, Seed, WorldGenerator}, world_gen::{get_world_gen, Seed, WorldGenerator},
}; };

View File

@ -2,13 +2,11 @@ pub mod biome;
pub mod block; pub mod block;
pub mod chunk; pub mod chunk;
pub mod coordinates; pub mod coordinates;
pub mod cylindrical_chunk_iterator;
pub mod dimension; pub mod dimension;
pub mod global_registry; pub mod global_registry;
pub mod item; pub mod item;
pub mod level; pub mod level;
pub mod radial_chunk_iterator;
pub mod vector2;
pub mod vector3;
mod world_gen; mod world_gen;
pub const WORLD_HEIGHT: usize = 384; pub const WORLD_HEIGHT: usize = 384;

View File

@ -1,59 +0,0 @@
use crate::vector2::Vector2;
pub struct RadialIterator {
radius: u32,
direction: usize,
current: Vector2<i32>,
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<i32>;
fn next(&mut self) -> Option<Self::Item> {
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)
}
}

View File

@ -1,10 +1,10 @@
use pumpkin_core::math::vector2::Vector2;
use static_assertions::assert_obj_safe; use static_assertions::assert_obj_safe;
use crate::biome::Biome; use crate::biome::Biome;
use crate::block::BlockId; use crate::block::BlockId;
use crate::chunk::ChunkData; use crate::chunk::ChunkData;
use crate::coordinates::{BlockCoordinates, XZBlockCoordinates}; use crate::coordinates::{BlockCoordinates, XZBlockCoordinates};
use crate::vector2::Vector2;
use crate::world_gen::Seed; use crate::world_gen::Seed;
pub trait GeneratorInit { pub trait GeneratorInit {

View File

@ -1,7 +1,8 @@
use pumpkin_core::math::vector2::Vector2;
use crate::{ use crate::{
chunk::{ChunkBlocks, ChunkData}, chunk::{ChunkBlocks, ChunkData},
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates}, coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
vector2::Vector2,
WORLD_LOWEST_Y, WORLD_MAX_Y, WORLD_LOWEST_Y, WORLD_MAX_Y,
}; };

View File

@ -34,6 +34,7 @@ mod client_packet;
mod container; mod container;
pub mod player_packet; pub mod player_packet;
#[derive(Clone)]
pub struct PlayerConfig { pub struct PlayerConfig {
pub locale: String, // 16 pub locale: String, // 16
pub view_distance: i8, pub view_distance: i8,

View File

@ -4,11 +4,15 @@ use crate::{
commands::{handle_command, CommandSender}, commands::{handle_command, CommandSender},
entity::player::{ChatMode, Hand, Player}, entity::player::{ChatMode, Hand, Player},
server::Server, server::Server,
util::math::wrap_degrees, world::player_chunker,
}; };
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use pumpkin_config::ADVANCED_CONFIG; 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_entity::EntityId;
use pumpkin_inventory::WindowType; use pumpkin_inventory::WindowType;
use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem}; use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem};
@ -18,7 +22,6 @@ use pumpkin_protocol::{
CHeadRot, CHurtAnimation, CPingResponse, CPlayerChatMessage, CUpdateEntityPos, CHeadRot, CHurtAnimation, CPingResponse, CPlayerChatMessage, CUpdateEntityPos,
CUpdateEntityPosRot, CUpdateEntityRot, CWorldEvent, FilterType, CUpdateEntityPosRot, CUpdateEntityRot, CWorldEvent, FilterType,
}, },
position::WorldPosition,
server::play::{ server::play::{
Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, Action, ActionType, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport,
SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition,
@ -46,9 +49,7 @@ impl Player {
if let Some((id, position)) = self.awaiting_teleport.as_ref() { if let Some((id, position)) = self.awaiting_teleport.as_ref() {
if id == &confirm_teleport.teleport_id { 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 // 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.set_pos(position.x, position.y, position.z);
self.entity.y = position.y;
self.entity.z = position.z;
self.awaiting_teleport = None; self.awaiting_teleport = None;
} else { } else {
@ -75,21 +76,24 @@ impl Player {
return; return;
} }
let entity = &mut self.entity; let entity = &mut self.entity;
entity.lastx = entity.x; self.lastx = entity.pos.x;
entity.lasty = entity.y; self.lasty = entity.pos.y;
entity.lastz = entity.z; self.lastz = entity.pos.z;
entity.x = Self::clamp_horizontal(position.x); entity.set_pos(
entity.y = Self::clamp_vertical(position.feet_y); Self::clamp_horizontal(position.x),
entity.z = Self::clamp_horizontal(position.z); Self::clamp_vertical(position.feet_y),
Self::clamp_horizontal(position.z),
);
// TODO: teleport when moving > 8 block // TODO: teleport when moving > 8 block
// send new position to all other players // send new position to all other players
let on_ground = self.on_ground; let on_ground = self.on_ground;
let entity_id = entity.entity_id; let entity_id = entity.entity_id;
let (x, lastx) = (entity.x, entity.lastx); let (x, lastx) = (entity.pos.x, self.lastx);
let (y, lasty) = (entity.y, entity.lasty); let (y, lasty) = (entity.pos.y, self.lasty);
let (z, lastz) = (entity.z, entity.lastz); let (z, lastz) = (entity.pos.z, self.lastz);
let world = self.world.lock().await; let world = self.world.clone();
let world = world.lock().await;
world.broadcast_packet( world.broadcast_packet(
&[&self.client.token], &[&self.client.token],
&CUpdateEntityPos::new( &CUpdateEntityPos::new(
@ -100,6 +104,7 @@ impl Player {
on_ground, on_ground,
), ),
); );
player_chunker::update_position(&world, self).await;
} }
pub async fn handle_position_rotation( pub async fn handle_position_rotation(
@ -120,25 +125,28 @@ impl Player {
} }
let entity = &mut self.entity; let entity = &mut self.entity;
entity.lastx = entity.x; self.lastx = entity.pos.x;
entity.lasty = entity.y; self.lasty = entity.pos.y;
entity.lastz = entity.z; self.lastz = entity.pos.z;
entity.x = Self::clamp_horizontal(position_rotation.x); entity.set_pos(
entity.y = Self::clamp_vertical(position_rotation.feet_y); Self::clamp_horizontal(position_rotation.x),
entity.z = Self::clamp_horizontal(position_rotation.z); Self::clamp_vertical(position_rotation.feet_y),
Self::clamp_horizontal(position_rotation.z),
);
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0; entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0; entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0;
// send new position to all other players // send new position to all other players
let on_ground = self.on_ground; let on_ground = self.on_ground;
let entity_id = entity.entity_id; let entity_id = entity.entity_id;
let (x, lastx) = (entity.x, entity.lastx); let (x, lastx) = (entity.pos.x, self.lastx);
let (y, lasty) = (entity.y, entity.lasty); let (y, lasty) = (entity.pos.y, self.lasty);
let (z, lastz) = (entity.z, entity.lastz); let (z, lastz) = (entity.pos.z, self.lastz);
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0); let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
let pitch = modulus(entity.pitch * 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 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( world.broadcast_packet(
&[&self.client.token], &[&self.client.token],
@ -156,6 +164,8 @@ impl Player {
&[&self.client.token], &[&self.client.token],
&CHeadRot::new(entity_id.into(), yaw as u8), &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) { 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()), Hand::from_i32(client_information.main_hand.into()),
ChatMode::from_i32(client_information.chat_mode.into()), ChatMode::from_i32(client_information.chat_mode.into()),
) { ) {
self.client.config = Some(PlayerConfig { self.config = PlayerConfig {
locale: client_information.locale, locale: client_information.locale,
view_distance: client_information.view_distance, view_distance: client_information.view_distance,
chat_mode, chat_mode,
@ -308,7 +318,7 @@ impl Player {
main_hand, main_hand,
text_filtering: client_information.text_filtering, text_filtering: client_information.text_filtering,
server_listing: client_information.server_listing, server_listing: client_information.server_listing,
}); };
} else { } else {
self.kick(TextComponent::text("Invalid hand or chat type")) self.kick(TextComponent::text("Invalid hand or chat type"))
} }

View File

@ -2,7 +2,11 @@ use std::sync::Arc;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_traits::ToPrimitive; 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_entity::{entity_type::EntityType, pose::EntityPose, Entity, EntityId};
use pumpkin_inventory::player::PlayerInventory; use pumpkin_inventory::player::PlayerInventory;
use pumpkin_protocol::{ use pumpkin_protocol::{
@ -11,7 +15,6 @@ use pumpkin_protocol::{
CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetEntityMetadata, CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSetEntityMetadata,
CSyncPlayerPosition, CSystemChatMessage, Metadata, PlayerAction, CSyncPlayerPosition, CSystemChatMessage, Metadata, PlayerAction,
}, },
position::WorldPosition,
server::play::{ server::play::{
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
@ -20,12 +23,10 @@ use pumpkin_protocol::{
}, },
ConnectionState, RawPacket, ServerPacket, VarInt, ConnectionState, RawPacket, ServerPacket, VarInt,
}; };
use pumpkin_world::vector3::Vector3;
use crate::{ use crate::{
client::{authentication::GameProfile, Client}, client::{authentication::GameProfile, Client, PlayerConfig},
server::Server, server::Server,
util::boundingbox::BoundingBox,
world::World, world::World,
}; };
@ -55,6 +56,7 @@ pub struct Player {
pub gameprofile: GameProfile, pub gameprofile: GameProfile,
pub client: Client, pub client: Client,
pub entity: Entity, pub entity: Entity,
pub config: PlayerConfig,
// TODO: Put this into entity // TODO: Put this into entity
pub world: Arc<tokio::sync::Mutex<World>>, pub world: Arc<tokio::sync::Mutex<World>>,
/// Current gamemode /// Current gamemode
@ -67,6 +69,9 @@ pub struct Player {
/// send `send_abilties_update` when changed /// send `send_abilties_update` when changed
pub abilities: PlayerAbilities, pub abilities: PlayerAbilities,
pub lastx: f64,
pub lasty: f64,
pub lastz: f64,
// Client side value, Should be not trusted // Client side value, Should be not trusted
pub on_ground: bool, pub on_ground: bool,
@ -82,6 +87,8 @@ pub struct Player {
pub teleport_id_count: i32, pub teleport_id_count: i32,
// Current awaiting teleport id and location, None if did not teleport // Current awaiting teleport id and location, None if did not teleport
pub awaiting_teleport: Option<(VarInt, Vector3<f64>)>, pub awaiting_teleport: Option<(VarInt, Vector3<f64>)>,
pub watched_section: Vector3<i32>,
} }
impl Player { impl Player {
@ -103,8 +110,9 @@ impl Player {
} }
} }
}; };
let config = client.config.clone().unwrap_or_default();
Self { Self {
config,
gameprofile, gameprofile,
client, client,
entity: Entity::new(entity_id, EntityType::Player, 1.62), entity: Entity::new(entity_id, EntityType::Player, 1.62),
@ -123,6 +131,10 @@ impl Player {
teleport_id_count: 0, teleport_id_count: 0,
abilities: PlayerAbilities::default(), abilities: PlayerAbilities::default(),
gamemode, 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); assert!(self.sneaking != sneaking);
self.sneaking = sneaking; self.sneaking = sneaking;
self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await;
if sneaking { // if sneaking {
self.set_pose(EntityPose::Crouching).await; // self.set_pose(EntityPose::Crouching).await;
} else { // } else {
self.set_pose(EntityPose::Standing).await; // self.set_pose(EntityPose::Standing).await;
} // }
} }
pub async fn set_sprinting(&mut self, sprinting: bool) { pub async fn set_sprinting(&mut self, sprinting: bool) {
@ -242,12 +254,10 @@ impl Player {
self.teleport_id_count = 0; self.teleport_id_count = 0;
} }
let entity = &mut self.entity; let entity = &mut self.entity;
entity.x = x; entity.set_pos(x, y, z);
entity.y = y; self.lastx = x;
entity.z = z; self.lasty = y;
entity.lastx = x; self.lastz = z;
entity.lasty = y;
entity.lastz = z;
entity.yaw = yaw; entity.yaw = yaw;
entity.pitch = pitch; entity.pitch = pitch;
self.awaiting_teleport = Some((self.teleport_id_count.into(), Vector3::new(x, y, z))); 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 d = self.block_interaction_range() + additional_range;
let box_pos = BoundingBox::from_block(pos); let box_pos = BoundingBox::from_block(pos);
box_pos.squared_magnitude(Vector3 { box_pos.squared_magnitude(Vector3 {
x: self.entity.x, x: self.entity.pos.x,
y: self.entity.y + self.entity.standing_eye_height as f64, y: self.entity.pos.y + self.entity.standing_eye_height as f64,
z: self.entity.z, z: self.entity.pos.z,
}) < d * d }) < d * d
} }
@ -441,13 +451,13 @@ impl Player {
} }
} }
#[derive(FromPrimitive)] #[derive(FromPrimitive, Clone)]
pub enum Hand { pub enum Hand {
Main, Main,
Off, Off,
} }
#[derive(FromPrimitive)] #[derive(FromPrimitive, Clone)]
pub enum ChatMode { pub enum ChatMode {
Enabled, Enabled,
CommandsOnly, CommandsOnly,

View File

@ -1,2 +1 @@
pub mod boundingbox;
pub mod math;

View File

@ -4,23 +4,25 @@ use std::{
sync::{Arc, Mutex, MutexGuard}, sync::{Arc, Mutex, MutexGuard},
}; };
pub mod player_chunker;
use mio::Token; use mio::Token;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use pumpkin_config::BasicConfiguration; use pumpkin_config::BasicConfiguration;
use pumpkin_core::math::vector2::Vector2;
use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_entity::{entity_type::EntityType, EntityId};
use pumpkin_protocol::{ use pumpkin_protocol::{
client::play::{ client::play::{
CCenterChunk, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities,
CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, PlayerAction,
PlayerAction,
}, },
uuid::UUID, uuid::UUID,
ClientPacket, VarInt, ClientPacket, VarInt,
}; };
use pumpkin_world::{level::Level, radial_chunk_iterator::RadialIterator}; use pumpkin_world::level::Level;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::entity::player::Player; use crate::{client::Client, entity::player::Player};
pub struct World { pub struct World {
pub level: Arc<Mutex<Level>>, pub level: Arc<Mutex<Level>>,
@ -90,7 +92,7 @@ impl World {
// TODO: this is for debug purpose, remove later // TODO: this is for debug purpose, remove later
player player
.client .client
.send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1)); .send_packet(&CPlayerAbilities::new(0x02, 0.4, 0.1));
// teleport // teleport
let x = 10.0; let x = 10.0;
@ -191,12 +193,12 @@ impl World {
existing_player.entity_id().into(), existing_player.entity_id().into(),
UUID(gameprofile.id), UUID(gameprofile.id),
(EntityType::Player as i32).into(), (EntityType::Player as i32).into(),
entity.x, entity.pos.x,
entity.y, entity.pos.y,
entity.z, entity.pos.z,
entity.yaw, entity.yaw,
entity.pitch, entity.pitch,
entity.pitch, entity.head_yaw,
0.into(), 0.into(),
0.0, 0.0,
0.0, 0.0,
@ -214,26 +216,28 @@ impl World {
self.broadcast_packet(&[&player.client.token], &packet) self.broadcast_packet(&[&player.client.token], &packet)
} }
self.spawn_test_chunk(player, base_config.view_distance as u32) // Spawn in inital chunks
.await; 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<Vector2<i32>>,
distance: i32,
) {
let inst = std::time::Instant::now(); let inst = std::time::Instant::now();
let (sender, mut chunk_receiver) = mpsc::channel(distance as usize); let (sender, mut chunk_receiver) = mpsc::channel(distance as usize);
let chunks: Vec<_> = RadialIterator::new(distance).collect();
let level = self.level.clone(); let level = self.level.clone();
tokio::spawn(async move { tokio::spawn(async move {
level.lock().unwrap().fetch_chunks(&chunks, sender); 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 { while let Some(chunk_data) = chunk_receiver.recv().await {
if client.closed {
return;
}
// dbg!(chunk_pos); // dbg!(chunk_pos);
let chunk_data = match chunk_data { let chunk_data = match chunk_data {
Ok(d) => d, Ok(d) => d,
@ -252,10 +256,9 @@ impl World {
len / (1024 * 1024) len / (1024 * 1024)
); );
} }
player.client.send_packet(&CChunkData(&chunk_data)); client.send_packet(&CChunkData(&chunk_data));
} }
let t = inst.elapsed(); dbg!("DONE CHUNKS", inst.elapsed());
dbg!("DONE", t);
} }
/// TODO: This definitly should be in world /// TODO: This definitly should be in world
@ -283,12 +286,10 @@ impl World {
// todo: put this into the entitiy struct // todo: put this into the entitiy struct
let id = player.entity_id(); let id = player.entity_id();
let uuid = player.gameprofile.id; let uuid = player.gameprofile.id;
dbg!("1");
self.broadcast_packet( self.broadcast_packet(
&[&player.client.token], &[&player.client.token],
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
); );
dbg!("2");
self.broadcast_packet(&[&player.client.token], &CRemoveEntities::new(&[id.into()])) self.broadcast_packet(&[&player.client.token], &CRemoveEntities::new(&[id.into()]))
} }
} }

View File

@ -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<i32> {
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),
)
}