From a75ea9ed11f8b23d55b5db47053cedc42aff15f3 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sun, 19 Apr 2026 17:47:40 +0200 Subject: [PATCH] fix: wrong entity spawner values --- pumpkin-plugin-wit/v0.1/event.wit | 1 - pumpkin-world/src/level.rs | 199 ++++++++++-------- pumpkin-world/src/lighting/runtime.rs | 140 ++++++------ pumpkin/src/block/blocks/piston/mod.rs | 3 +- pumpkin/src/block/blocks/redstone/turbo.rs | 12 +- pumpkin/src/entity/area_effect_cloud.rs | 2 +- pumpkin/src/entity/experience_orb.rs | 8 +- pumpkin/src/entity/falling.rs | 8 +- pumpkin/src/entity/item.rs | 22 +- pumpkin/src/entity/living.rs | 28 +-- pumpkin/src/entity/mob/mod.rs | 6 +- pumpkin/src/entity/mod.rs | 14 +- pumpkin/src/entity/player.rs | 3 +- pumpkin/src/entity/projectile/arrow.rs | 2 +- pumpkin/src/entity/projectile/egg.rs | 2 +- pumpkin/src/entity/projectile/ender_pearl.rs | 2 +- pumpkin/src/entity/projectile/eye_of_ender.rs | 2 +- .../src/entity/projectile/firework_rocket.rs | 2 +- .../src/entity/projectile/lingering_potion.rs | 2 +- pumpkin/src/entity/projectile/mod.rs | 2 +- pumpkin/src/entity/projectile/snowball.rs | 2 +- .../src/entity/projectile/splash_potion.rs | 2 +- pumpkin/src/entity/projectile/wind_charge.rs | 2 +- pumpkin/src/entity/tnt.rs | 8 +- pumpkin/src/entity/vehicle/boat.rs | 2 +- pumpkin/src/net/java/play.rs | 21 +- .../events/player/player_interact_event.rs | 7 - .../wasm/wasm_host/wit/v0_1/events/player.rs | 12 -- pumpkin/src/world/mod.rs | 139 +++++++----- pumpkin/src/world/natural_spawner.rs | 38 ++-- 30 files changed, 364 insertions(+), 329 deletions(-) diff --git a/pumpkin-plugin-wit/v0.1/event.wit b/pumpkin-plugin-wit/v0.1/event.wit index 4cd6362d2..cb826895c 100644 --- a/pumpkin-plugin-wit/v0.1/event.wit +++ b/pumpkin-plugin-wit/v0.1/event.wit @@ -150,7 +150,6 @@ interface event { player: player, action: interact-action, clicked-pos: option, - item: item-stack, block: string, cancelled: bool } diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 78b1d1a89..a1851a7cb 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -337,18 +337,6 @@ impl Level { self.chunk_saver.block_and_await_ongoing_tasks().await; self.entity_saver.block_and_await_ongoing_tasks().await; - // save all chunks currently in memory - // let chunks_to_write = self - // .loaded_chunks - // .iter() - // .map(|chunk| (*chunk.key(), chunk.value().clone())) - // .collect::>(); - // self.loaded_chunks.clear(); - - // TODO: I think the chunk_saver should be at the server level - // self.chunk_saver.clear_watched_chunks().await; - // self.write_chunks(chunks_to_write).await; - // save all chunks currently in memory let chunks_to_write = self .loaded_entity_chunks @@ -383,9 +371,6 @@ impl Level { .or_insert(1); } - // self.chunk_saver - // .watch_chunks(&self.level_folder, chunks) - // .await; self.entity_saver .watch_chunks(&self.level_folder, chunks) .await; @@ -539,23 +524,25 @@ impl Level { self.chunk_watchers.shrink_to_fit(); } - // if the difference is too big, we can shrink the loaded chunks - // (1024 chunks is the equivalent to a 32x32 chunks area) - // if self.loaded_chunks.capacity() - self.loaded_chunks.len() >= 4096 { - // self.loaded_chunks.shrink_to_fit(); - // } - if self.loaded_entity_chunks.capacity() - self.loaded_entity_chunks.len() >= 4096 { self.loaded_entity_chunks.shrink_to_fit(); } } - pub async fn get_chunk(self: &Arc, pos: Vector2) -> SyncChunk { + pub async fn get_or_fetch_chunk R>( + self: &Arc, + pos: Vector2, + f: F, + ) -> R { // Check if already in memory - if let Some(chunk) = self.loaded_chunks.get(&pos) { - return chunk.clone(); + if let Some(res) = self.read_chunk_sync(&pos, &f) { + return res; } + let chunk = self.fetch_chunk(pos).await; + f(&chunk) + } + async fn fetch_chunk(self: &Arc, pos: Vector2) -> SyncChunk { let recv = self.chunk_listener.add_single_chunk_listener(pos); { @@ -564,12 +551,9 @@ impl Level { lock.send_change(); }; - let chunk = if let Some(chunk) = self.loaded_chunks.get(&pos) { - chunk.clone() - } else { - recv.await - .expect("Chunk listener dropped without sending chunk") - }; + let chunk = recv + .await + .expect("Chunk listener dropped without sending chunk"); { let mut lock = self.chunk_loading.lock().unwrap(); @@ -694,31 +678,30 @@ impl Level { pub async fn get_block_state(self: &Arc, position: &BlockPos) -> RawBlockState { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let chunk = self.get_chunk(chunk_coordinate).await; - - let Some(id) = chunk.section.get_block_absolute_y( - relative.x as usize, - relative.y, - relative.z as usize, - ) else { - return RawBlockState(Block::VOID_AIR.default_state.id); - }; - - RawBlockState(id) + let id = self + .get_or_fetch_chunk(chunk_coordinate, |chunk| { + chunk.section.get_block_absolute_y( + relative.x as usize, + relative.y, + relative.z as usize, + ) + }) + .await; + RawBlockState(id.unwrap_or(Block::VOID_AIR.default_state.id)) } + pub async fn get_rough_biome(self: &Arc, position: &BlockPos) -> &'static Biome { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let chunk = self.get_chunk(chunk_coordinate).await; - - let Some(id) = chunk.section.get_rough_biome_absolute_y( - relative.x as usize, - relative.y, - relative.z as usize, - ) else { - return &Biome::THE_VOID; - }; - - Biome::from_id(id).unwrap() + let id = self + .get_or_fetch_chunk(chunk_coordinate, |chunk| { + chunk.section.get_rough_biome_absolute_y( + relative.x as usize, + relative.y, + relative.z as usize, + ) + }) + .await; + Biome::from_id(id.unwrap_or(0)).unwrap_or(&Biome::THE_VOID) } pub async fn set_block_state( @@ -727,17 +710,19 @@ impl Level { block_state_id: BlockStateId, ) -> BlockStateId { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let chunk = self.get_chunk(chunk_coordinate).await; - let replaced_block_state_id = chunk.section.set_block_absolute_y( - relative.x as usize, - relative.y, - relative.z as usize, - block_state_id, - ); - if replaced_block_state_id != block_state_id { - chunk.mark_dirty(true); - } - replaced_block_state_id + self.get_or_fetch_chunk(chunk_coordinate, |chunk| { + let replaced_block_state_id = chunk.section.set_block_absolute_y( + relative.x as usize, + relative.y, + relative.z as usize, + block_state_id, + ); + if replaced_block_state_id != block_state_id { + chunk.mark_dirty(true); + } + replaced_block_state_id + }) + .await } pub async fn write_chunks(&self, chunks_to_write: Vec<(Vector2, SyncChunk)>) { @@ -780,6 +765,40 @@ impl Level { .map(|x| x.value().clone()) } + pub fn is_chunk_loaded(&self, coordinates: &Vector2) -> bool { + self.loaded_chunks.contains_key(coordinates) + } + + pub fn read_chunk_sync R>( + &self, + coordinates: &Vector2, + f: F, + ) -> Option { + self.loaded_chunks.get(coordinates).map(|x| f(x.value())) + } + + pub fn read_entity_chunk_sync R>( + &self, + coordinates: &Vector2, + f: F, + ) -> Option { + self.loaded_entity_chunks + .get(coordinates) + .map(|x| f(x.value())) + } + + pub async fn get_or_fetch_entity_chunk R>( + self: &Arc, + pos: Vector2, + f: F, + ) -> R { + if let Some(res) = self.read_entity_chunk_sync(&pos, &f) { + return res; + } + let chunk = self.get_entity_chunk(pos).await; + f(&chunk) + } + pub fn try_get_entity_chunk( &self, coordinates: Vector2, @@ -794,17 +813,18 @@ impl Level { delay: u8, priority: TickPriority, ) { - let chunk = self.get_chunk(block_pos.chunk_position()).await; let tick_order = self.schedule_tick_counts.fetch_add(1, Ordering::Relaxed); - chunk.block_ticks.schedule_tick( - &ScheduledTick { - delay, - position: block_pos, - priority, - value: unsafe { &*std::ptr::from_ref::(block) }, - }, - tick_order, - ); + let scheduled_tick = ScheduledTick { + delay, + position: block_pos, + priority, + value: unsafe { &*std::ptr::from_ref::(block) }, + }; + + self.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { + chunk.block_ticks.schedule_tick(&scheduled_tick, tick_order); + }) + .await; } pub async fn schedule_fluid_tick( @@ -814,17 +834,18 @@ impl Level { delay: u8, priority: TickPriority, ) { - let chunk = self.get_chunk(block_pos.chunk_position()).await; let tick_order = self.schedule_tick_counts.fetch_add(1, Ordering::Relaxed); - chunk.fluid_ticks.schedule_tick( - &ScheduledTick { - delay, - position: block_pos, - priority, - value: unsafe { &*std::ptr::from_ref::(fluid) }, - }, - tick_order, - ); + let scheduled_tick = ScheduledTick { + delay, + position: block_pos, + priority, + value: unsafe { &*std::ptr::from_ref::(fluid) }, + }; + + self.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { + chunk.fluid_ticks.schedule_tick(&scheduled_tick, tick_order); + }) + .await; } pub async fn is_block_tick_scheduled( @@ -832,8 +853,10 @@ impl Level { block_pos: &BlockPos, block: &Block, ) -> bool { - let chunk = self.get_chunk(block_pos.chunk_position()).await; - chunk.block_ticks.is_scheduled(*block_pos, block) + self.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { + chunk.block_ticks.is_scheduled(*block_pos, block) + }) + .await } pub async fn is_fluid_tick_scheduled( @@ -841,7 +864,9 @@ impl Level { block_pos: &BlockPos, fluid: &Fluid, ) -> bool { - let chunk = self.get_chunk(block_pos.chunk_position()).await; - chunk.fluid_ticks.is_scheduled(*block_pos, fluid) + self.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { + chunk.fluid_ticks.is_scheduled(*block_pos, fluid) + }) + .await } } diff --git a/pumpkin-world/src/lighting/runtime.rs b/pumpkin-world/src/lighting/runtime.rs index c8b6cc416..b53f8e9f5 100644 --- a/pumpkin-world/src/lighting/runtime.rs +++ b/pumpkin-world/src/lighting/runtime.rs @@ -454,20 +454,22 @@ impl DynamicLightEngine { ) -> Option { let (chunk_pos, relative) = position.chunk_and_chunk_relative_position(); - let chunk = level.get_chunk(chunk_pos).await; + level + .get_or_fetch_chunk(chunk_pos, |chunk| { + let section_idx = (relative.y - chunk.section.min_y) as usize / 16; + let light_engine = chunk.light_engine.lock().ok()?; - let section_idx = (relative.y - chunk.section.min_y) as usize / 16; - let light_engine = chunk.light_engine.lock().ok()?; - - light_engine - .block_light - .get(section_idx)? - .get( - relative.x as usize, - (relative.y - chunk.section.min_y) as usize % 16, - relative.z as usize, - ) - .into() + light_engine + .block_light + .get(section_idx)? + .get( + relative.x as usize, + (relative.y - chunk.section.min_y) as usize % 16, + relative.z as usize, + ) + .into() + }) + .await } pub async fn set_block_light_level( @@ -477,43 +479,51 @@ impl DynamicLightEngine { light_level: u8, ) -> Result<(), String> { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let chunk = level.get_chunk(chunk_coordinate).await; - let section_index = (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; - // Bounds check for section index - let mut light_engine = chunk.light_engine.lock().unwrap(); - if section_index >= light_engine.block_light.len() { - return Err("Invalid section index".to_string()); - } - let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE; - light_engine.block_light[section_index].set( - relative.x as usize, - relative_y, - relative.z as usize, - light_level, - ); - // Mark chunk as dirty so lighting changes are saved to disk - if !chunk.is_dirty() { - chunk.mark_dirty(true); - } - Ok(()) + level + .get_or_fetch_chunk(chunk_coordinate, |chunk| { + let section_index = + (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; + // Bounds check for section index + let mut light_engine = chunk.light_engine.lock().unwrap(); + if section_index >= light_engine.block_light.len() { + return Err("Invalid section index".to_string()); + } + let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE; + light_engine.block_light[section_index].set( + relative.x as usize, + relative_y, + relative.z as usize, + light_level, + ); + // Mark chunk as dirty so lighting changes are saved to disk + if !chunk.is_dirty() { + chunk.mark_dirty(true); + } + Ok(()) + }) + .await } pub async fn get_sky_light_level(&self, level: &Arc, position: &BlockPos) -> u8 { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let chunk = level.get_chunk(chunk_coordinate).await; - let section_index = (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; + level + .get_or_fetch_chunk(chunk_coordinate, |chunk| { + let section_index = + (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; - let light_engine = chunk.light_engine.lock().unwrap(); - // Bounds check for section index (lock the light engine) - if section_index >= light_engine.sky_light.len() { - return 15; - } + let light_engine = chunk.light_engine.lock().unwrap(); + // Bounds check for section index (lock the light engine) + if section_index >= light_engine.sky_light.len() { + return 15; + } - light_engine.sky_light[section_index].get( - relative.x as usize, - (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE, - relative.z as usize, - ) + light_engine.sky_light[section_index].get( + relative.x as usize, + (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE, + relative.z as usize, + ) + }) + .await } pub async fn set_sky_light_level( @@ -523,24 +533,28 @@ impl DynamicLightEngine { light_level: u8, ) -> Result<(), String> { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let chunk = level.get_chunk(chunk_coordinate).await; - let section_index = (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; - // Bounds check for section index - let mut light_engine = chunk.light_engine.lock().unwrap(); - if section_index >= light_engine.sky_light.len() { - return Err("Invalid section index".to_string()); - } - let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE; - light_engine.sky_light[section_index].set( - relative.x as usize, - relative_y, - relative.z as usize, - light_level, - ); - // Mark chunk as dirty so lighting changes are saved to disk - if !chunk.is_dirty() { - chunk.mark_dirty(true); - } - Ok(()) + level + .get_or_fetch_chunk(chunk_coordinate, |chunk| { + let section_index = + (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; + // Bounds check for section index + let mut light_engine = chunk.light_engine.lock().unwrap(); + if section_index >= light_engine.sky_light.len() { + return Err("Invalid section index".to_string()); + } + let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE; + light_engine.sky_light[section_index].set( + relative.x as usize, + relative_y, + relative.z as usize, + light_level, + ); + // Mark chunk as dirty so lighting changes are saved to disk + if !chunk.is_dirty() { + chunk.mark_dirty(true); + } + Ok(()) + }) + .await } } diff --git a/pumpkin/src/block/blocks/piston/mod.rs b/pumpkin/src/block/blocks/piston/mod.rs index b2fbba2f1..f985c1258 100644 --- a/pumpkin/src/block/blocks/piston/mod.rs +++ b/pumpkin/src/block/blocks/piston/mod.rs @@ -67,7 +67,8 @@ impl<'a> PistonHandler<'a> { if !self.try_move(self.pos_to, self.motion_direction).await { return false; } - for block_pos in self.moved_blocks.clone() { + for i in 0..self.moved_blocks.len() { + let block_pos = self.moved_blocks[i]; let block = self.world.get_block(&block_pos).await; if Self::is_block_sticky(block) && !self.try_move_adjacent_block(block, &block_pos).await diff --git a/pumpkin/src/block/blocks/redstone/turbo.rs b/pumpkin/src/block/blocks/redstone/turbo.rs index f23968e6d..fbccb5b57 100644 --- a/pumpkin/src/block/blocks/redstone/turbo.rs +++ b/pumpkin/src/block/blocks/redstone/turbo.rs @@ -289,10 +289,13 @@ impl RedstoneWireTurbo { self.current_walk_layer = 1; while !self.update_queue[0].is_empty() || !self.update_queue[1].is_empty() { - for node_id in self.update_queue[0].clone() { + let mut current_queue = self.update_queue.remove(0); + self.update_queue.push(Vec::new()); + + for node_id in ¤t_queue { let block = Block::from_state_id(self.nodes[node_id.index].state.id); if block == &Block::REDSTONE_WIRE { - self.update_node(world, node_id, self.current_walk_layer) + self.update_node(world, *node_id, self.current_walk_layer) .await; } else { // This only works because updating any other block than a wire will @@ -304,7 +307,10 @@ impl RedstoneWireTurbo { } } - self.shift_queue(); + current_queue.clear(); + let last_idx = self.update_queue.len() - 1; + self.update_queue[last_idx] = current_queue; + self.current_walk_layer += 1; } diff --git a/pumpkin/src/entity/area_effect_cloud.rs b/pumpkin/src/entity/area_effect_cloud.rs index 71fd12414..a011cbe23 100644 --- a/pumpkin/src/entity/area_effect_cloud.rs +++ b/pumpkin/src/entity/area_effect_cloud.rs @@ -206,7 +206,7 @@ impl EntityBase for AreaEffectCloudEntity { #[allow(clippy::semicolon_outside_block)] fn tick<'a>( &'a self, - _caller: Arc, + _caller: &'a Arc, _server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { diff --git a/pumpkin/src/entity/experience_orb.rs b/pumpkin/src/entity/experience_orb.rs index 8cf3613c3..2e137f5db 100644 --- a/pumpkin/src/entity/experience_orb.rs +++ b/pumpkin/src/entity/experience_orb.rs @@ -70,12 +70,12 @@ impl NBTStorage for ExperienceOrbEntity {} impl EntityBase for ExperienceOrbEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { let entity = &self.entity; - entity.tick(caller.clone(), server).await; + entity.tick(caller, server).await; let bounding_box = entity.bounding_box.load(); let original_velo = entity.velocity.load(); @@ -95,9 +95,9 @@ impl EntityBase for ExperienceOrbEntity { entity.velocity.store(velo); - entity.move_entity(caller.clone(), velo).await; + entity.move_entity(caller, velo).await; - entity.tick_block_collisions(&caller, server).await; + entity.tick_block_collisions(caller, server).await; let age = self.orb_age.fetch_add(1, Ordering::Relaxed); if age >= 6000 { diff --git a/pumpkin/src/entity/falling.rs b/pumpkin/src/entity/falling.rs index f55abb585..4fc74d543 100644 --- a/pumpkin/src/entity/falling.rs +++ b/pumpkin/src/entity/falling.rs @@ -50,12 +50,12 @@ impl NBTStorage for FallingEntity {} impl EntityBase for FallingEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { let entity = &self.entity; - entity.tick(caller.clone(), server).await; + entity.tick(caller, server).await; let original_velo = entity.velocity.load(); let mut velo = original_velo; @@ -63,8 +63,8 @@ impl EntityBase for FallingEntity { entity.velocity.store(velo); - entity.move_entity(caller.clone(), velo).await; - entity.tick_block_collisions(&caller, server).await; + entity.move_entity(caller, velo).await; + entity.tick_block_collisions(caller, server).await; if entity.on_ground.load(Ordering::Relaxed) { entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); entity diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index 962bd868d..5377cf8e9 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -271,15 +271,15 @@ impl ItemEntity { Some(tick_move) } - async fn move_and_apply_friction( - &self, - caller: &Arc, - server: &Server, + async fn move_and_apply_friction<'a>( + &'a self, + caller: &'a Arc, + server: &'a Server, move_velo: Vector3, ) { let entity = &self.entity; - entity.move_entity(caller.clone(), move_velo).await; + entity.move_entity(caller, move_velo).await; entity.tick_block_collisions(caller, server).await; let mut friction = 0.98; @@ -332,9 +332,9 @@ impl ItemEntity { true } - async fn sync_motion_if_dirty( - &self, - caller: &Arc, + async fn sync_motion_if_dirty<'a>( + &'a self, + caller: &'a Arc, original_velo: Vector3, ) { let entity = &self.entity; @@ -358,7 +358,7 @@ impl NBTStorage for ItemEntity {} impl EntityBase for ItemEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { @@ -379,12 +379,12 @@ impl EntityBase for ItemEntity { }; if tick_move { - self.move_and_apply_friction(&caller, server, move_velo) + self.move_and_apply_friction(caller, server, move_velo) .await; } if self.process_age_and_merge().await { - self.sync_motion_if_dirty(&caller, original_velo).await; + self.sync_motion_if_dirty(caller, original_velo).await; } }) } diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 17e4e87ad..b56cc1433 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -728,7 +728,7 @@ impl LivingEntity { .await; } - async fn tick_movement(&self, server: &Server, caller: Arc) { + async fn tick_movement<'a>(&'a self, server: &'a Server, caller: &'a Arc) { if self.jumping_cooldown.load(Relaxed) != 0 { self.jumping_cooldown.fetch_sub(1, Relaxed); } @@ -799,24 +799,24 @@ impl LivingEntity { && should_swim_in_fluids && self.entity.entity_type != &EntityType::STRIDER { - self.travel_in_fluid(caller.clone(), touching_water).await; + self.travel_in_fluid(caller, touching_water).await; } else { // TODO: Gliding - self.travel_in_air(caller.clone()).await; + self.travel_in_air(caller).await; } // TODO: Apply Soul Speed boot durability when tick_block_underneath is implemented. //self.entity.tick_block_underneath(&caller); - let suffocating = self.entity.tick_block_collisions(&caller, server).await; + let suffocating = self.entity.tick_block_collisions(caller, server).await; if suffocating { - self.damage(&*caller, 1.0, DamageType::IN_WALL).await; + self.damage(&**caller, 1.0, DamageType::IN_WALL).await; } } - async fn travel_in_air(&self, caller: Arc) { + async fn travel_in_air<'a>(&'a self, caller: &'a Arc) { // applyMovementInput let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED); @@ -853,7 +853,7 @@ impl LivingEntity { self.apply_climbing_speed(); - self.make_move(caller.clone()).await; + self.make_move(caller).await; let mut velo = self.entity.velocity.load(); @@ -874,7 +874,7 @@ impl LivingEntity { if let Some(lev) = levitation { velo.y += 0.05f64.mul_add(f64::from(lev.amplifier + 1), -velo.y) * 0.2; } else { - velo.y -= self.get_effective_gravity(&caller).await; + velo.y -= self.get_effective_gravity(caller).await; // TODO: If world is not loaded: replace effective gravity with: @@ -898,11 +898,11 @@ impl LivingEntity { self.entity.velocity.store(velo); } - async fn travel_in_fluid(&self, caller: Arc, water: bool) { + async fn travel_in_fluid<'a>(&'a self, caller: &'a Arc, water: bool) { let movement_input = self.movement_input.load(); let falling = self.entity.velocity.load().y <= 0.0; - let gravity = self.get_effective_gravity(&caller).await; + let gravity = self.get_effective_gravity(caller).await; let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED); if water { @@ -995,7 +995,7 @@ impl LivingEntity { } } - async fn make_move(&self, caller: Arc) { + async fn make_move<'a>(&'a self, caller: &'a Arc) { self.entity .move_entity(caller, self.entity.velocity.load()) .await; @@ -2028,11 +2028,11 @@ impl EntityBase for LivingEntity { #[allow(clippy::too_many_lines)] fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { - self.entity.tick(caller.clone(), server).await; + self.entity.tick(caller, server).await; // Only tick movement if the entity is alive. This prevents a dead "corpse" // from continuing to be simulated (accumulating fall_distance/velocity). @@ -2041,7 +2041,7 @@ impl EntityBase for LivingEntity { let in_death_animation = self.health.load() <= 0.0 && self.death_time.load(Relaxed) < 20; if is_alive || (in_death_animation && self.entity.entity_type != &EntityType::PLAYER) { - self.tick_movement(server, caller.clone()).await; + self.tick_movement(server, caller).await; // Vanilla-like order: freeze logic runs after movement/collisions. self.entity.tick_frozen(caller.as_ref()).await; } diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index e51ba2e83..ad271fe21 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -306,11 +306,10 @@ pub trait Mob: EntityBase + Send + Sync { false } } - impl EntityBase for T { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { @@ -319,11 +318,12 @@ impl EntityBase for T { if mob_entity.breeding_cooldown.load(Relaxed) > 0 { mob_entity.breeding_cooldown.fetch_sub(1, Relaxed); } + if mob_entity.love_ticks.load(Relaxed) > 0 { mob_entity.love_ticks.fetch_sub(1, Relaxed); } - self.mob_tick(&caller).await; + self.mob_tick(caller).await; // AI runs before physics (vanilla order: goals → navigator → look → physics) let age = mob_entity.living_entity.entity.age.load(Relaxed); diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 205813ec9..4f8d3116e 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -117,7 +117,7 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any { /// The `server` parameter provides access to the game server instance. fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { @@ -1508,7 +1508,7 @@ impl Entity { // Move by a delta, adjust for collisions, and send // Does not send movement. That must be done separately - async fn move_entity(&self, caller: Arc, mut motion: Vector3) { + async fn move_entity<'a>(&'a self, caller: &'a Arc, mut motion: Vector3) { if caller.get_player().is_some() { return; } @@ -2753,7 +2753,7 @@ impl NBTStorage for Entity { impl EntityBase for Entity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, _server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { @@ -2763,9 +2763,9 @@ impl EntityBase for Entity { .store(was_in_powder_snow, Ordering::Relaxed); self.is_in_powder_snow.store(false, Ordering::Relaxed); self.update_last_pos(); - self.tick_portal(&caller).await; - self.update_fluid_state(&caller).await; - self.check_out_of_world(&*caller).await; + self.tick_portal(caller).await; + self.update_fluid_state(caller).await; + self.check_out_of_world(&**caller).await; let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); // Check for fire immunity (or if the specific entity is) @@ -2779,7 +2779,7 @@ impl EntityBase for Entity { } } else { if fire_ticks % 20 == 0 { - caller.damage(&*caller, 1.0, DamageType::ON_FIRE).await; + (**caller).damage(&**caller, 1.0, DamageType::ON_FIRE).await; } self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 095b53a7a..cb892f124 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1597,7 +1597,8 @@ impl Player { self.last_attacked_ticks.fetch_add(1, Ordering::Relaxed); - self.living_entity.tick(self.clone(), server).await; + let caller: Arc = self.clone(); + self.living_entity.tick(&caller, server).await; // Vanilla updates pose in PlayerEntity#tick after super.tick(). self.update_player_pose().await; self.breath_manager.tick(self).await; diff --git a/pumpkin/src/entity/projectile/arrow.rs b/pumpkin/src/entity/projectile/arrow.rs index 68406d9f1..906784670 100644 --- a/pumpkin/src/entity/projectile/arrow.rs +++ b/pumpkin/src/entity/projectile/arrow.rs @@ -191,7 +191,7 @@ impl EntityBase for ArrowEntity { #[allow(clippy::too_many_lines)] fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, _server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { diff --git a/pumpkin/src/entity/projectile/egg.rs b/pumpkin/src/entity/projectile/egg.rs index a2b64644f..a61368638 100644 --- a/pumpkin/src/entity/projectile/egg.rs +++ b/pumpkin/src/entity/projectile/egg.rs @@ -86,7 +86,7 @@ impl EntityBase for EggEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { self.thrown.process_tick(caller, server).await }) diff --git a/pumpkin/src/entity/projectile/ender_pearl.rs b/pumpkin/src/entity/projectile/ender_pearl.rs index a2aa5482e..66567721a 100644 --- a/pumpkin/src/entity/projectile/ender_pearl.rs +++ b/pumpkin/src/entity/projectile/ender_pearl.rs @@ -43,7 +43,7 @@ impl NBTStorage for EnderPearlEntity {} impl EntityBase for EnderPearlEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { self.thrown.process_tick(caller, server).await }) diff --git a/pumpkin/src/entity/projectile/eye_of_ender.rs b/pumpkin/src/entity/projectile/eye_of_ender.rs index 27f8cedbc..72743eb29 100644 --- a/pumpkin/src/entity/projectile/eye_of_ender.rs +++ b/pumpkin/src/entity/projectile/eye_of_ender.rs @@ -123,7 +123,7 @@ impl NBTStorage for EyeOfEnder {} impl EntityBase for EyeOfEnder { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { diff --git a/pumpkin/src/entity/projectile/firework_rocket.rs b/pumpkin/src/entity/projectile/firework_rocket.rs index ad0d811a9..19fe32427 100644 --- a/pumpkin/src/entity/projectile/firework_rocket.rs +++ b/pumpkin/src/entity/projectile/firework_rocket.rs @@ -103,7 +103,7 @@ impl NBTStorage for FireworkRocketEntity {} impl EntityBase for FireworkRocketEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { diff --git a/pumpkin/src/entity/projectile/lingering_potion.rs b/pumpkin/src/entity/projectile/lingering_potion.rs index 248fe4f96..0ae274297 100644 --- a/pumpkin/src/entity/projectile/lingering_potion.rs +++ b/pumpkin/src/entity/projectile/lingering_potion.rs @@ -82,7 +82,7 @@ impl EntityBase for LingeringPotionEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { self.thrown.process_tick(caller, server).await }) diff --git a/pumpkin/src/entity/projectile/mod.rs b/pumpkin/src/entity/projectile/mod.rs index e8c8b283d..4a932ae42 100644 --- a/pumpkin/src/entity/projectile/mod.rs +++ b/pumpkin/src/entity/projectile/mod.rs @@ -103,7 +103,7 @@ impl NBTStorage for ThrownItemEntity {} impl ThrownItemEntity { /// Process a tick for projectile movement and collisions - pub async fn process_tick(&self, caller: Arc, _server: &Server) { + pub async fn process_tick<'a>(&'a self, caller: &'a Arc, _server: &'a Server) { let entity = self.get_entity(); let world = entity.world.load(); diff --git a/pumpkin/src/entity/projectile/snowball.rs b/pumpkin/src/entity/projectile/snowball.rs index 5b89bffa9..42dc0e83c 100644 --- a/pumpkin/src/entity/projectile/snowball.rs +++ b/pumpkin/src/entity/projectile/snowball.rs @@ -45,7 +45,7 @@ impl NBTStorage for SnowballEntity {} impl EntityBase for SnowballEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { self.thrown.process_tick(caller, server).await }) diff --git a/pumpkin/src/entity/projectile/splash_potion.rs b/pumpkin/src/entity/projectile/splash_potion.rs index 846393b4a..bb1e35238 100644 --- a/pumpkin/src/entity/projectile/splash_potion.rs +++ b/pumpkin/src/entity/projectile/splash_potion.rs @@ -123,7 +123,7 @@ impl EntityBase for SplashPotionEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { self.thrown.process_tick(caller, server).await }) diff --git a/pumpkin/src/entity/projectile/wind_charge.rs b/pumpkin/src/entity/projectile/wind_charge.rs index cd819fa07..dc614a070 100644 --- a/pumpkin/src/entity/projectile/wind_charge.rs +++ b/pumpkin/src/entity/projectile/wind_charge.rs @@ -75,7 +75,7 @@ impl NBTStorage for WindChargeEntity {} impl EntityBase for WindChargeEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { diff --git a/pumpkin/src/entity/tnt.rs b/pumpkin/src/entity/tnt.rs index a5b894bb0..b53d98da4 100644 --- a/pumpkin/src/entity/tnt.rs +++ b/pumpkin/src/entity/tnt.rs @@ -36,7 +36,7 @@ impl NBTStorage for TNTEntity {} impl EntityBase for TNTEntity { fn tick<'a>( &'a self, - caller: Arc, + caller: &'a Arc, server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { @@ -46,8 +46,8 @@ impl EntityBase for TNTEntity { let mut velo = original_velo; velo.y -= self.get_gravity(); - entity.move_entity(caller.clone(), velo).await; - entity.tick_block_collisions(&caller, server).await; + entity.move_entity(caller, velo).await; + entity.tick_block_collisions(caller, server).await; entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); if entity.on_ground.load(Ordering::Relaxed) { @@ -73,7 +73,7 @@ impl EntityBase for TNTEntity { } else { // Safe decrement self.fuse.store(fuse - 1, Relaxed); - entity.update_fluid_state(&caller).await; + entity.update_fluid_state(caller).await; } }) } diff --git a/pumpkin/src/entity/vehicle/boat.rs b/pumpkin/src/entity/vehicle/boat.rs index f1153ab9a..c1e1fd16c 100644 --- a/pumpkin/src/entity/vehicle/boat.rs +++ b/pumpkin/src/entity/vehicle/boat.rs @@ -104,7 +104,7 @@ impl EntityBase for BoatEntity { fn tick<'a>( &'a self, - _caller: Arc, + _caller: &'a Arc, _server: &'a Server, ) -> EntityBaseFuture<'a, ()> { Box::pin(async move { diff --git a/pumpkin/src/net/java/play.rs b/pumpkin/src/net/java/play.rs index 89be7276b..47d4094d8 100644 --- a/pumpkin/src/net/java/play.rs +++ b/pumpkin/src/net/java/play.rs @@ -1197,9 +1197,6 @@ impl JavaClient { return; }; - let inventory = player.inventory(); - let item = inventory.held_item(); - let (yaw, pitch) = player.rotation(); let hit_result = player .world() @@ -1219,18 +1216,11 @@ impl JavaClient { PlayerInteractEvent::new( player, InteractAction::LeftClickBlock, - &item, player.world().get_block(&hit_pos).await, Some(hit_pos), ) } else { - PlayerInteractEvent::new( - player, - InteractAction::LeftClickAir, - &item, - &Block::AIR, - None, - ) + PlayerInteractEvent::new(player, InteractAction::LeftClickAir, &Block::AIR, None) }; let server = player.world().server.upgrade().unwrap(); @@ -2264,18 +2254,11 @@ impl JavaClient { PlayerInteractEvent::new( player, InteractAction::RightClickBlock, - &item_in_hand, player.world().get_block(&hit_pos).await, Some(hit_pos), ) } else { - PlayerInteractEvent::new( - player, - InteractAction::RightClickAir, - &item_in_hand, - &Block::AIR, - None, - ) + PlayerInteractEvent::new(player, InteractAction::RightClickAir, &Block::AIR, None) }; self.prepare_hand_item_for_use(player, hand, &item_in_hand) .await; diff --git a/pumpkin/src/plugin/api/events/player/player_interact_event.rs b/pumpkin/src/plugin/api/events/player/player_interact_event.rs index e618d6d07..d7fe7b14b 100644 --- a/pumpkin/src/plugin/api/events/player/player_interact_event.rs +++ b/pumpkin/src/plugin/api/events/player/player_interact_event.rs @@ -2,10 +2,8 @@ use std::sync::Arc; use crate::entity::player::Player; use pumpkin_data::Block; -use pumpkin_data::item_stack::ItemStack; use pumpkin_macros::{Event, cancellable}; use pumpkin_util::math::position::BlockPos; -use tokio::sync::Mutex; use super::PlayerEvent; @@ -26,9 +24,6 @@ pub struct PlayerInteractEvent { /// The position of the block that was clicked, if any. pub clicked_pos: Option, - /// The item in the player's hand at the time of interaction. - pub item: Arc>, - /// The block that was interacted with. pub block: &'static Block, } @@ -50,14 +45,12 @@ impl PlayerInteractEvent { pub fn new( player: &Arc, action: InteractAction, - item: &Arc>, block: &'static Block, clicked_pos: Option, ) -> Self { Self { player: Arc::clone(player), action, - item: Arc::clone(item), block, clicked_pos, cancelled: false, diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/player.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/player.rs index b92d507aa..054c0f1a8 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/player.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/player.rs @@ -576,22 +576,10 @@ impl ToFromWasmEvent for PlayerInteractEvent { .add_player(self.player.clone()) .expect("failed to add player resource"); - let stack = self.item.blocking_lock(); - let item = crate::plugin::loader::wasm::wasm_host::wit::v0_1::player::to_wit_item_stack( - &stack, - ) - .unwrap_or( - crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::ItemStack { - registry_key: "minecraft:air".to_string(), - count: 0, - }, - ); - Event::PlayerInteractEvent(PlayerInteractEventData { player, action: to_wasm_interact_action(&self.action), clicked_pos: self.clicked_pos.map(to_wasm_block_position), - item, block: self.block.name.to_string(), cancelled: self.cancelled, }) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index b0383c668..5f1e38e9b 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -300,6 +300,7 @@ impl World { entity.write_nbt(&mut nbt).await; if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() { let old_chunk = old_chunk.to_vec2_i32(); + let chunk = self.level.get_entity_chunk(old_chunk).await; chunk.mark_dirty(true); let mut data = chunk.data.lock().await; @@ -776,7 +777,7 @@ impl World { for entity in entities_to_tick.iter() { entity.get_entity().age.fetch_add(1, Relaxed); - entity.tick(entity.clone(), server).await; + entity.tick(entity, server).await; for player in players.iter() { if player @@ -989,12 +990,14 @@ impl World { ) }; let spawn_passives = self.level_time.lock().await.time_of_day % 400 == 0; + let spawn_enemies = !peaceful && spawn_monsters && spawn_mobs; + let spawn_passives = spawn_passives && spawn_mobs; let spawn_list: Vec<&'static MobCategory> = natural_spawner::get_filtered_spawning_categories( &spawn_state, spawn_mobs, - peaceful && spawn_mobs && spawn_monsters, - peaceful && spawn_mobs && spawn_passives && spawn_monsters, + spawn_enemies, + spawn_passives, ); // log::debug!("spawning list size {}", spawn_list.len()); @@ -1454,12 +1457,15 @@ impl World { /// Gets the `MOTION_BLOCKING` heightmap value for a given XZ position. pub async fn get_motion_blocking_height(&self, x: i32, z: i32) -> i32 { let chunk_pos = Vector2::new(x >> 4, z >> 4); - let chunk = self.level.get_chunk(chunk_pos).await; - chunk - .heightmap - .lock() - .unwrap() - .get(MotionBlocking, x, z, self.min_y) + self.level + .get_or_fetch_chunk(chunk_pos, |chunk| { + chunk + .heightmap + .lock() + .unwrap() + .get(MotionBlocking, x, z, self.min_y) + }) + .await } #[allow(clippy::too_many_lines)] @@ -2388,7 +2394,10 @@ impl World { // Ensure at least the center chunk is sent synchronously before teleport. if let crate::net::ClientPlatform::Java(java_client) = &player.client { let center_chunk = player.living_entity.entity.chunk_pos.load(); - let chunk = target_world.level.get_chunk(center_chunk).await; + let chunk = target_world + .level + .get_or_fetch_chunk(center_chunk, std::clone::Clone::clone) + .await; java_client.send_packet_now(&CChunkBatchStart).await; java_client.send_packet_now(&CChunkData(&chunk)).await; java_client @@ -2520,14 +2529,18 @@ impl World { let mut data = chunk.data.lock().await; if old_chunk == current_chunk_coordinate { - data.insert(base_entity.entity_uuid, nbt); + data.insert(base_entity.entity_uuid, nbt.clone()); return; } // The chunk has changed, lets remove the entity from the old chunk data.remove(&base_entity.entity_uuid); } - chunk.data.lock().await.insert(base_entity.entity_uuid, nbt); + chunk + .data + .lock() + .await + .insert(base_entity.entity_uuid, nbt.clone()); chunk.mark_dirty(true); } @@ -3000,13 +3013,12 @@ impl World { pub async fn add_entity_silent(&self, entity: Arc) { let base_entity = entity.get_entity(); let chunk_coordinate = base_entity.block_pos.load().chunk_position(); + let mut nbt = PNbtCompound::new(); + entity.write_nbt(&mut nbt).await; + let chunk = self.level.get_entity_chunk(chunk_coordinate).await; - { - let mut nbt = PNbtCompound::new(); - entity.write_nbt(&mut nbt).await; - chunk.data.lock().await.insert(base_entity.entity_uuid, nbt); - chunk.mark_dirty(true); - }; + chunk.data.lock().await.insert(base_entity.entity_uuid, nbt); + chunk.mark_dirty(true); self.entities.rcu(|current_entities| { let mut new_entities = (**current_entities).clone(); @@ -3048,23 +3060,26 @@ impl World { flags: BlockFlags, ) -> BlockStateId { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); - let level = &self.level; - let chunk = level.get_chunk(chunk_coordinate).await; + let replaced_block_state_id = self + .level + .get_or_fetch_chunk(chunk_coordinate, |chunk| { + let replaced_block_state_id = chunk.section.set_block_absolute_y( + relative.x as usize, + relative.y, + relative.z as usize, + block_state_id, + ); + // Mark chunk dirty if it isn't already + if replaced_block_state_id != block_state_id && !chunk.is_dirty() { + chunk.mark_dirty(true); + } + replaced_block_state_id + }) + .await; - let replaced_block_state_id = chunk.section.set_block_absolute_y( - relative.x as usize, - relative.y, - relative.z as usize, - block_state_id, - ); if replaced_block_state_id == block_state_id { return block_state_id; } - // Mark chunk dirty if it isn't already - if !chunk.is_dirty() { - chunk.mark_dirty(true); - } - drop(chunk); self.unsent_block_changes .lock() @@ -3172,9 +3187,9 @@ impl World { let (_chunk_coordinate, _) = position.chunk_and_chunk_relative_position(); - level + self.level .light_engine - .update_lighting_at(level, *position) + .update_lighting_at(&self.level, *position) .await; replaced_block_state_id @@ -3660,19 +3675,21 @@ impl World { } pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option> { - let chunk = self.level.get_chunk(block_pos.chunk_position()).await; - chunk.block_entities.lock().unwrap().get(block_pos).cloned() + self.level + .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { + chunk.block_entities.lock().unwrap().get(block_pos).cloned() + }) + .await } pub async fn add_block_entity(&self, block_entity: Arc) { let block_pos = block_entity.get_position(); - let chunk = self.level.get_chunk(block_pos.chunk_position()).await; + let chunk_pos = block_pos.chunk_position(); let block_entity_nbt = block_entity.chunk_data_nbt(); if let Some(nbt) = &block_entity_nbt { let mut bytes = Vec::new(); to_bytes_unnamed(nbt, &mut bytes).unwrap(); - let chunk_pos = block_pos.chunk_position(); self.broadcast_to_chunk( chunk_pos, &CBlockEntityData::new( @@ -3684,36 +3701,42 @@ impl World { .await; } - chunk - .block_entities - .lock() - .unwrap() - .insert(block_pos, block_entity); - chunk.mark_dirty(true); + self.level + .get_or_fetch_chunk(chunk_pos, |chunk| { + chunk + .block_entities + .lock() + .unwrap() + .insert(block_pos, block_entity.clone()); + chunk.mark_dirty(true); + }) + .await; } pub async fn remove_block_entity(&self, block_pos: &BlockPos) { - let chunk = self.level.get_chunk(block_pos.chunk_position()).await; - if chunk - .block_entities - .lock() - .unwrap() - .remove(block_pos) - .is_some() - { - chunk.mark_dirty(true); - } + self.level + .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { + if chunk + .block_entities + .lock() + .unwrap() + .remove(block_pos) + .is_some() + { + chunk.mark_dirty(true); + } + }) + .await; } pub async fn update_block_entity(&self, block_entity: &Arc) { let block_pos = block_entity.get_position(); - let chunk = self.level.get_chunk(block_pos.chunk_position()).await; + let chunk_pos = block_pos.chunk_position(); let block_entity_nbt = block_entity.chunk_data_nbt(); if let Some(nbt) = &block_entity_nbt { let mut bytes = Vec::new(); to_bytes_unnamed(nbt, &mut bytes).unwrap(); - let chunk_pos = block_pos.chunk_position(); self.broadcast_to_chunk( chunk_pos, &CBlockEntityData::new( @@ -3724,7 +3747,11 @@ impl World { ) .await; } - chunk.mark_dirty(true); + self.level + .get_or_fetch_chunk(chunk_pos, |chunk| { + chunk.mark_dirty(true); + }) + .await; } fn intersects_aabb_with_direction( diff --git a/pumpkin/src/world/natural_spawner.rs b/pumpkin/src/world/natural_spawner.rs index 9ceb43df1..de56f47e9 100644 --- a/pumpkin/src/world/natural_spawner.rs +++ b/pumpkin/src/world/natural_spawner.rs @@ -434,30 +434,28 @@ pub async fn spawn_category_for_position( prepared_data.push((base_entity.entity_uuid, nbt, packet, entity.clone())); } - { - let chunk_handle = world.level.get_entity_chunk(*chunk_pos).await; - let mut data = chunk_handle.data.lock().await; + let chunk_handle = world.level.get_entity_chunk(*chunk_pos).await; + let mut data = chunk_handle.data.lock().await; - for (uuid, nbt, _, _) in &prepared_data { - data.insert(*uuid, nbt.clone()); - } - drop(data); - chunk_handle.mark_dirty(true); + for (uuid, nbt, _, _) in &prepared_data { + data.insert(*uuid, nbt.clone()); + } + drop(data); + chunk_handle.mark_dirty(true); - world.entities.rcu(|current_entities| { - let mut new_entities = (**current_entities).clone(); + world.entities.rcu(|current_entities| { + let mut new_entities = (**current_entities).clone(); - for (uuid, _, _, entity_ref) in &prepared_data { - if !new_entities - .iter() - .any(|e| e.get_entity().entity_uuid == *uuid) - { - new_entities.push(entity_ref.clone()); - } + for (uuid, _, _, entity_ref) in &prepared_data { + if !new_entities + .iter() + .any(|e| e.get_entity().entity_uuid == *uuid) + { + new_entities.push(entity_ref.clone()); } - new_entities - }); - }; + } + new_entities + }); for (_, _, packet, _) in prepared_data { world.broadcast_packet_all(&packet).await;