Pumpkin/pumpkin/src/world/player_chunker.rs
Snowiiii e0dd8d6b61 Move Chunks things from Client to Player
Also made Chunk handling more robust
2024-10-28 22:48:17 +01:00

169 lines
6.0 KiB
Rust

use std::sync::Arc;
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;
pub async fn get_view_distance(player: &Player) -> i8 {
player
.config
.lock()
.await
.view_distance
.clamp(2, BASIC_CONFIG.view_distance as i8)
}
pub async fn player_join(world: &World, player: Arc<Player>) {
let new_watched = chunk_section_from_pos(&player.living_entity.entity.block_pos.load());
player.watched_section.store(new_watched);
let chunk_pos = player.living_entity.entity.chunk_pos.load();
assert_eq!(new_watched.x, chunk_pos.x);
assert_eq!(new_watched.z, chunk_pos.z);
log::debug!("Sending center chunk to {}", player.gameprofile.name);
player
.client
.send_packet(&CCenterChunk {
chunk_x: chunk_pos.x.into(),
chunk_z: chunk_pos.z.into(),
})
.await;
let view_distance = i32::from(get_view_distance(&player).await);
log::debug!(
"Player {} ({}) joined with view distance: {}",
player.gameprofile.name,
player.gameprofile.name,
view_distance
);
let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance);
let loading_chunks = new_cylindrical.all_chunks_within();
world.spawn_world_chunks(player, &loading_chunks);
}
pub async fn update_position(player: &Arc<Player>) {
let entity = &player.living_entity.entity;
let current_watched = player.watched_section.load();
let new_watched = chunk_section_from_pos(&entity.block_pos.load());
player.watched_section.store(new_watched);
if current_watched != new_watched {
//log::debug!("changing chunks");
let chunk_pos = entity.chunk_pos.load();
assert_eq!(new_watched.x, chunk_pos.x);
assert_eq!(new_watched.z, chunk_pos.z);
player
.client
.send_packet(&CCenterChunk {
chunk_x: chunk_pos.x.into(),
chunk_z: chunk_pos.z.into(),
})
.await;
let view_distance = i32::from(get_view_distance(player).await);
let old_cylindrical = Cylindrical::new(
Vector2::new(current_watched.x, current_watched.z),
view_distance,
);
let new_cylindrical = Cylindrical::new(chunk_pos, view_distance);
let mut loading_chunks = Vec::new();
let mut unloading_chunks = Vec::new();
Cylindrical::for_each_changed_chunk(
old_cylindrical,
new_cylindrical,
|chunk_pos| {
loading_chunks.push(chunk_pos);
},
|chunk_pos| {
unloading_chunks.push(chunk_pos);
},
);
if !loading_chunks.is_empty() {
//let inst = std::time::Instant::now();
entity
.world
.spawn_world_chunks(player.clone(), &loading_chunks);
//log::debug!("Loading chunks took {:?}", inst.elapsed());
}
if !unloading_chunks.is_empty() {
// We want to check if this chunk is still pending
// if it is -> ignore
//let inst = std::time::Instant::now();
let watched_chunks: Vec<_> = {
let mut pending_chunks = player.pending_chunks.lock();
unloading_chunks
.into_iter()
.filter(|chunk| {
if let Some(handles) = pending_chunks.get_mut(chunk) {
if let Some((count, handle)) = handles
.iter_mut()
.rev()
.enumerate()
.find(|(_, handle)| !handle.aborted())
{
log::debug!("Aborting chunk {:?} ({}) (unload)", chunk, count);
// We want to abort the last queued chunk, that we if a client still
// has a pending request for this chunk, we dont need to do the work
// twice
handle.abort();
} else {
log::warn!(
"Aborting chunk {:?} but all were already aborted!",
chunk
);
}
false
} else {
true
}
})
.collect()
};
//log::debug!("Unloading chunks took {:?} (1)", inst.elapsed());
let chunks_to_clean = entity.world.mark_chunks_as_not_watched(&watched_chunks);
entity.world.clean_chunks(&chunks_to_clean);
//log::debug!("Unloading chunks took {:?} (2)", inst.elapsed());
// This can take a little if we are sending a bunch of packets, queue it up :p
let client = player.client.clone();
tokio::spawn(async move {
for chunk in watched_chunks {
if client.closed.load(std::sync::atomic::Ordering::Relaxed) {
// We will never un-close a connection
break;
}
client
.send_packet(&CUnloadChunk::new(chunk.x, chunk.z))
.await;
}
});
//log::debug!("Unloading chunks took {:?} (3)", inst.elapsed());
}
}
}
#[must_use]
pub const 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),
)
}