From 6f18d16581a3c2ea5260353eae86120dd727293c Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 16 Jan 2026 17:48:30 +0100 Subject: [PATCH] fix: the chunk unloading issue at least hopefully, i think it was an Ticket Overlap, By adding the new ticket before removing the old one, the ChunkLoading system always sees at least one valid path for the chunks that overlap between the two view distances. The fix seems to made the system slower but atleast it seems to not bug anymore --- pumpkin-world/src/chunk_system.rs | 9 ++- pumpkin-world/src/generation/proto_chunk.rs | 7 ++ pumpkin-world/src/level.rs | 41 +++++----- pumpkin-world/src/world.rs | 5 ++ .../blocks/redstone/abstract_redstone_gate.rs | 2 +- .../src/block/blocks/redstone/comparator.rs | 2 +- pumpkin/src/block/blocks/redstone/repeater.rs | 2 +- pumpkin/src/block/registry.rs | 8 +- pumpkin/src/entity/player.rs | 81 ++++++++++++++----- pumpkin/src/world/mod.rs | 7 ++ 10 files changed, 114 insertions(+), 50 deletions(-) diff --git a/pumpkin-world/src/chunk_system.rs b/pumpkin-world/src/chunk_system.rs index e05a5dd3b..75da0cb2a 100644 --- a/pumpkin-world/src/chunk_system.rs +++ b/pumpkin-world/src/chunk_system.rs @@ -19,7 +19,7 @@ use crate::generation::proto_chunk::{GenerationCache, TerrainCache}; use crate::generation::settings::{GenerationSettings, gen_settings_from_dimension}; use crate::level::{Level, SyncChunk}; use crate::world::{BlockAccessor, BlockRegistryExt}; -use crate::{GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters}; +use crate::{BlockStateId, GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters}; use crossbeam::channel::{Receiver, Sender}; use dashmap::DashMap; use itertools::Itertools; @@ -898,6 +898,13 @@ impl BlockAccessor for Cache { Box::pin(async move { GenerationCache::get_block_state(self, &position.0).to_state() }) } + fn get_block_state_id<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { GenerationCache::get_block_state(self, &position.0).0 }) + } + fn get_block_and_state<'a>( &'a self, position: &'a BlockPos, diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index a7561b6c1..742eaaecc 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -1133,6 +1133,13 @@ impl BlockAccessor for ProtoChunk { Box::pin(async move { self.get_block_state(&position.0).to_state() }) } + fn get_block_state_id<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { self.get_block_state(&position.0).0 }) + } + fn get_block_and_state<'a>( &'a self, position: &'a BlockPos, diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 69281f903..f306f272f 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -598,30 +598,31 @@ impl Level { } pub async fn get_chunk(self: &Arc, pos: Vector2) -> SyncChunk { - // Already loaded? + // Check if already in memory if let Some(chunk) = self.loaded_chunks.get(&pos) { + return chunk.clone(); + } + + log::debug!("Missing Chunk {pos:?}. Fetching."); + let clock = Instant::now(); + let recv = self.chunk_listener.add_single_chunk_listener(pos); + + { + let mut lock = self.chunk_loading.lock().unwrap(); + lock.add_ticket(pos, 31); + lock.send_change(); + } + + let ret = if let Some(chunk) = self.loaded_chunks.get(&pos) { chunk.clone() } else { - log::debug!("Missing Chunk {pos:?}. Fetching."); - let clock = Instant::now(); - let recv = self.chunk_listener.add_single_chunk_listener(pos); - { - let mut lock = self.chunk_loading.lock().unwrap(); - lock.add_force_ticket(pos); - lock.send_change(); - } + recv.await + .expect("Chunk listener dropped without sending chunk") + }; - let ret = if let Some(chunk) = self.loaded_chunks.get(&pos) { - // try again here. otherwise deadlock - chunk.clone() - } else { - recv.await.unwrap() - }; - let mut lock = self.chunk_loading.lock().unwrap(); - lock.remove_force_ticket(pos); - log::debug!("Chunk {pos:?} received after {:?}.", Instant::now() - clock); - ret - } + log::debug!("Chunk {pos:?} received after {:?}.", Instant::now() - clock); + + ret } async fn load_single_entity_chunk( diff --git a/pumpkin-world/src/world.rs b/pumpkin-world/src/world.rs index 46b405337..46ba5dcd8 100644 --- a/pumpkin-world/src/world.rs +++ b/pumpkin-world/src/world.rs @@ -138,6 +138,11 @@ pub trait BlockAccessor: Send + Sync { position: &'a BlockPos, ) -> Pin + Send + 'a>>; + fn get_block_state_id<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>>; + fn get_block_and_state<'a>( &'a self, position: &'a BlockPos, diff --git a/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs b/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs index 8e599c44c..99a374e09 100644 --- a/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs +++ b/pumpkin/src/block/blocks/redstone/abstract_redstone_gate.rs @@ -241,7 +241,7 @@ pub trait RedstoneGateBlock, ) { view_distance += 1; - let mut lock = level.chunk_loading.lock().unwrap(); - lock.add_ticket( - center, - ChunkLoading::get_level_from_view_distance(view_distance), - ); - lock.remove_ticket( - self.center, - ChunkLoading::get_level_from_view_distance(self.view_distance), - ); - lock.send_change(); - drop(lock); - let view_distance = i32::from(view_distance); - self.chunk_sent - .retain(|pos| (pos.x - center.x).abs().max((pos.y - center.y).abs()) <= view_distance); + let old_center = self.center; + let old_view_distance = self.view_distance; + + { + let mut lock = level.chunk_loading.lock().unwrap(); + lock.add_ticket( + center, + ChunkLoading::get_level_from_view_distance(view_distance), + ); + + if old_center != center || old_view_distance != view_distance { + lock.remove_ticket( + old_center, + ChunkLoading::get_level_from_view_distance(old_view_distance), + ); + } + lock.send_change(); + }; + + let view_distance_i32 = i32::from(view_distance); + + let mut chunks_to_watch = Vec::new(); + let mut chunks_to_unwatch = Vec::new(); + + for pos in &self.chunk_sent { + if (pos.x - center.x).abs().max((pos.y - center.y).abs()) > view_distance_i32 { + chunks_to_unwatch.push(*pos); + } + } + + let level_clone = level.clone(); + let chunks_to_unwatch_clone = chunks_to_unwatch.clone(); + tokio::spawn(async move { + level_clone + .mark_chunks_as_not_watched(&chunks_to_unwatch_clone) + .await; + }); + + self.chunk_sent.retain(|pos| { + (pos.x - center.x).abs().max((pos.y - center.y).abs()) <= view_distance_i32 + }); + let mut new_queue = BinaryHeap::with_capacity(self.chunk_queue.len()); for node in &self.chunk_queue { let dst = (node.1.x - center.x).abs().max((node.1.y - center.y).abs()); - if dst <= view_distance { + if dst <= view_distance_i32 { new_queue.push(HeapNode(dst, node.1, node.2.clone())); } } + self.chunk_queue = new_queue; self.center = center; - self.view_distance = view_distance as u8; - for dx in (-view_distance)..=view_distance { - for dy in (-view_distance)..=view_distance { + self.view_distance = view_distance; + + for dx in (-view_distance_i32)..=view_distance_i32 { + for dy in (-view_distance_i32)..=view_distance_i32 { let new_pos = center.add_raw(dx, dy); - if !self.chunk_sent.contains(&new_pos) - && let Some(chunk) = level.loaded_chunks.get(&new_pos) - { - self.push_chunk(new_pos, chunk.value().clone()); + if !self.chunk_sent.contains(&new_pos) { + chunks_to_watch.push(new_pos); + if let Some(chunk) = level.loaded_chunks.get(&new_pos) { + self.push_chunk(new_pos, chunk.value().clone()); + } } } } + let level_clone = level.clone(); + tokio::spawn(async move { + level_clone + .mark_chunks_as_newly_watched(&chunks_to_watch) + .await; + }); } pub fn clean_up(&mut self, level: &Arc) { diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index f5f2025ba..569570773 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -3452,6 +3452,13 @@ impl BlockAccessor for World { Box::pin(async move { Self::get_block_state(self, position).await }) } + fn get_block_state_id<'a>( + &'a self, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> { + Box::pin(async move { Self::get_block_state_id(self, position).await }) + } + fn get_block_and_state<'a>( &'a self, position: &'a BlockPos,