fix: wrong entity spawner values

This commit is contained in:
Alexander Medvedev 2026-04-19 17:47:40 +02:00
parent 5627b182d6
commit a75ea9ed11
30 changed files with 364 additions and 329 deletions

View File

@ -150,7 +150,6 @@ interface event {
player: player, player: player,
action: interact-action, action: interact-action,
clicked-pos: option<block-position>, clicked-pos: option<block-position>,
item: item-stack,
block: string, block: string,
cancelled: bool cancelled: bool
} }

View File

@ -337,18 +337,6 @@ impl Level {
self.chunk_saver.block_and_await_ongoing_tasks().await; self.chunk_saver.block_and_await_ongoing_tasks().await;
self.entity_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::<Vec<_>>();
// 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 // save all chunks currently in memory
let chunks_to_write = self let chunks_to_write = self
.loaded_entity_chunks .loaded_entity_chunks
@ -383,9 +371,6 @@ impl Level {
.or_insert(1); .or_insert(1);
} }
// self.chunk_saver
// .watch_chunks(&self.level_folder, chunks)
// .await;
self.entity_saver self.entity_saver
.watch_chunks(&self.level_folder, chunks) .watch_chunks(&self.level_folder, chunks)
.await; .await;
@ -539,23 +524,25 @@ impl Level {
self.chunk_watchers.shrink_to_fit(); 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 { if self.loaded_entity_chunks.capacity() - self.loaded_entity_chunks.len() >= 4096 {
self.loaded_entity_chunks.shrink_to_fit(); self.loaded_entity_chunks.shrink_to_fit();
} }
} }
pub async fn get_chunk(self: &Arc<Self>, pos: Vector2<i32>) -> SyncChunk { pub async fn get_or_fetch_chunk<R, F: Fn(&SyncChunk) -> R>(
self: &Arc<Self>,
pos: Vector2<i32>,
f: F,
) -> R {
// Check if already in memory // Check if already in memory
if let Some(chunk) = self.loaded_chunks.get(&pos) { if let Some(res) = self.read_chunk_sync(&pos, &f) {
return chunk.clone(); return res;
} }
let chunk = self.fetch_chunk(pos).await;
f(&chunk)
}
async fn fetch_chunk(self: &Arc<Self>, pos: Vector2<i32>) -> SyncChunk {
let recv = self.chunk_listener.add_single_chunk_listener(pos); let recv = self.chunk_listener.add_single_chunk_listener(pos);
{ {
@ -564,12 +551,9 @@ impl Level {
lock.send_change(); lock.send_change();
}; };
let chunk = if let Some(chunk) = self.loaded_chunks.get(&pos) { let chunk = recv
chunk.clone() .await
} else { .expect("Chunk listener dropped without sending chunk");
recv.await
.expect("Chunk listener dropped without sending chunk")
};
{ {
let mut lock = self.chunk_loading.lock().unwrap(); let mut lock = self.chunk_loading.lock().unwrap();
@ -694,31 +678,30 @@ impl Level {
pub async fn get_block_state(self: &Arc<Self>, position: &BlockPos) -> RawBlockState { pub async fn get_block_state(self: &Arc<Self>, position: &BlockPos) -> RawBlockState {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = self.get_chunk(chunk_coordinate).await; let id = self
.get_or_fetch_chunk(chunk_coordinate, |chunk| {
let Some(id) = chunk.section.get_block_absolute_y( chunk.section.get_block_absolute_y(
relative.x as usize, relative.x as usize,
relative.y, relative.y,
relative.z as usize, relative.z as usize,
) else { )
return RawBlockState(Block::VOID_AIR.default_state.id); })
}; .await;
RawBlockState(id.unwrap_or(Block::VOID_AIR.default_state.id))
RawBlockState(id)
} }
pub async fn get_rough_biome(self: &Arc<Self>, position: &BlockPos) -> &'static Biome { pub async fn get_rough_biome(self: &Arc<Self>, position: &BlockPos) -> &'static Biome {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = self.get_chunk(chunk_coordinate).await; let id = self
.get_or_fetch_chunk(chunk_coordinate, |chunk| {
let Some(id) = chunk.section.get_rough_biome_absolute_y( chunk.section.get_rough_biome_absolute_y(
relative.x as usize, relative.x as usize,
relative.y, relative.y,
relative.z as usize, relative.z as usize,
) else { )
return &Biome::THE_VOID; })
}; .await;
Biome::from_id(id.unwrap_or(0)).unwrap_or(&Biome::THE_VOID)
Biome::from_id(id).unwrap()
} }
pub async fn set_block_state( pub async fn set_block_state(
@ -727,17 +710,19 @@ impl Level {
block_state_id: BlockStateId, block_state_id: BlockStateId,
) -> BlockStateId { ) -> BlockStateId {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = self.get_chunk(chunk_coordinate).await; self.get_or_fetch_chunk(chunk_coordinate, |chunk| {
let replaced_block_state_id = chunk.section.set_block_absolute_y( let replaced_block_state_id = chunk.section.set_block_absolute_y(
relative.x as usize, relative.x as usize,
relative.y, relative.y,
relative.z as usize, relative.z as usize,
block_state_id, block_state_id,
); );
if replaced_block_state_id != block_state_id { if replaced_block_state_id != block_state_id {
chunk.mark_dirty(true); chunk.mark_dirty(true);
} }
replaced_block_state_id replaced_block_state_id
})
.await
} }
pub async fn write_chunks(&self, chunks_to_write: Vec<(Vector2<i32>, SyncChunk)>) { pub async fn write_chunks(&self, chunks_to_write: Vec<(Vector2<i32>, SyncChunk)>) {
@ -780,6 +765,40 @@ impl Level {
.map(|x| x.value().clone()) .map(|x| x.value().clone())
} }
pub fn is_chunk_loaded(&self, coordinates: &Vector2<i32>) -> bool {
self.loaded_chunks.contains_key(coordinates)
}
pub fn read_chunk_sync<R, F: Fn(&SyncChunk) -> R>(
&self,
coordinates: &Vector2<i32>,
f: F,
) -> Option<R> {
self.loaded_chunks.get(coordinates).map(|x| f(x.value()))
}
pub fn read_entity_chunk_sync<R, F: Fn(&SyncEntityChunk) -> R>(
&self,
coordinates: &Vector2<i32>,
f: F,
) -> Option<R> {
self.loaded_entity_chunks
.get(coordinates)
.map(|x| f(x.value()))
}
pub async fn get_or_fetch_entity_chunk<R, F: Fn(&SyncEntityChunk) -> R>(
self: &Arc<Self>,
pos: Vector2<i32>,
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( pub fn try_get_entity_chunk(
&self, &self,
coordinates: Vector2<i32>, coordinates: Vector2<i32>,
@ -794,17 +813,18 @@ impl Level {
delay: u8, delay: u8,
priority: TickPriority, priority: TickPriority,
) { ) {
let chunk = self.get_chunk(block_pos.chunk_position()).await;
let tick_order = self.schedule_tick_counts.fetch_add(1, Ordering::Relaxed); let tick_order = self.schedule_tick_counts.fetch_add(1, Ordering::Relaxed);
chunk.block_ticks.schedule_tick( let scheduled_tick = ScheduledTick {
&ScheduledTick { delay,
delay, position: block_pos,
position: block_pos, priority,
priority, value: unsafe { &*std::ptr::from_ref::<Block>(block) },
value: unsafe { &*std::ptr::from_ref::<Block>(block) }, };
},
tick_order, 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( pub async fn schedule_fluid_tick(
@ -814,17 +834,18 @@ impl Level {
delay: u8, delay: u8,
priority: TickPriority, priority: TickPriority,
) { ) {
let chunk = self.get_chunk(block_pos.chunk_position()).await;
let tick_order = self.schedule_tick_counts.fetch_add(1, Ordering::Relaxed); let tick_order = self.schedule_tick_counts.fetch_add(1, Ordering::Relaxed);
chunk.fluid_ticks.schedule_tick( let scheduled_tick = ScheduledTick {
&ScheduledTick { delay,
delay, position: block_pos,
position: block_pos, priority,
priority, value: unsafe { &*std::ptr::from_ref::<Fluid>(fluid) },
value: unsafe { &*std::ptr::from_ref::<Fluid>(fluid) }, };
},
tick_order, 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( pub async fn is_block_tick_scheduled(
@ -832,8 +853,10 @@ impl Level {
block_pos: &BlockPos, block_pos: &BlockPos,
block: &Block, block: &Block,
) -> bool { ) -> bool {
let chunk = self.get_chunk(block_pos.chunk_position()).await; self.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| {
chunk.block_ticks.is_scheduled(*block_pos, block) chunk.block_ticks.is_scheduled(*block_pos, block)
})
.await
} }
pub async fn is_fluid_tick_scheduled( pub async fn is_fluid_tick_scheduled(
@ -841,7 +864,9 @@ impl Level {
block_pos: &BlockPos, block_pos: &BlockPos,
fluid: &Fluid, fluid: &Fluid,
) -> bool { ) -> bool {
let chunk = self.get_chunk(block_pos.chunk_position()).await; self.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| {
chunk.fluid_ticks.is_scheduled(*block_pos, fluid) chunk.fluid_ticks.is_scheduled(*block_pos, fluid)
})
.await
} }
} }

View File

@ -454,20 +454,22 @@ impl DynamicLightEngine {
) -> Option<u8> { ) -> Option<u8> {
let (chunk_pos, relative) = position.chunk_and_chunk_relative_position(); 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; light_engine
let light_engine = chunk.light_engine.lock().ok()?; .block_light
.get(section_idx)?
light_engine .get(
.block_light relative.x as usize,
.get(section_idx)? (relative.y - chunk.section.min_y) as usize % 16,
.get( relative.z as usize,
relative.x as usize, )
(relative.y - chunk.section.min_y) as usize % 16, .into()
relative.z as usize, })
) .await
.into()
} }
pub async fn set_block_light_level( pub async fn set_block_light_level(
@ -477,43 +479,51 @@ impl DynamicLightEngine {
light_level: u8, light_level: u8,
) -> Result<(), String> { ) -> Result<(), String> {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = level.get_chunk(chunk_coordinate).await; level
let section_index = (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; .get_or_fetch_chunk(chunk_coordinate, |chunk| {
// Bounds check for section index let section_index =
let mut light_engine = chunk.light_engine.lock().unwrap(); (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
if section_index >= light_engine.block_light.len() { // Bounds check for section index
return Err("Invalid section index".to_string()); let mut light_engine = chunk.light_engine.lock().unwrap();
} if section_index >= light_engine.block_light.len() {
let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE; return Err("Invalid section index".to_string());
light_engine.block_light[section_index].set( }
relative.x as usize, let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE;
relative_y, light_engine.block_light[section_index].set(
relative.z as usize, relative.x as usize,
light_level, relative_y,
); relative.z as usize,
// Mark chunk as dirty so lighting changes are saved to disk light_level,
if !chunk.is_dirty() { );
chunk.mark_dirty(true); // Mark chunk as dirty so lighting changes are saved to disk
} if !chunk.is_dirty() {
Ok(()) chunk.mark_dirty(true);
}
Ok(())
})
.await
} }
pub async fn get_sky_light_level(&self, level: &Arc<Level>, position: &BlockPos) -> u8 { pub async fn get_sky_light_level(&self, level: &Arc<Level>, position: &BlockPos) -> u8 {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = level.get_chunk(chunk_coordinate).await; level
let section_index = (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; .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(); let light_engine = chunk.light_engine.lock().unwrap();
// Bounds check for section index (lock the light engine) // Bounds check for section index (lock the light engine)
if section_index >= light_engine.sky_light.len() { if section_index >= light_engine.sky_light.len() {
return 15; return 15;
} }
light_engine.sky_light[section_index].get( light_engine.sky_light[section_index].get(
relative.x as usize, relative.x as usize,
(relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE, (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE,
relative.z as usize, relative.z as usize,
) )
})
.await
} }
pub async fn set_sky_light_level( pub async fn set_sky_light_level(
@ -523,24 +533,28 @@ impl DynamicLightEngine {
light_level: u8, light_level: u8,
) -> Result<(), String> { ) -> Result<(), String> {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = level.get_chunk(chunk_coordinate).await; level
let section_index = (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE; .get_or_fetch_chunk(chunk_coordinate, |chunk| {
// Bounds check for section index let section_index =
let mut light_engine = chunk.light_engine.lock().unwrap(); (relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
if section_index >= light_engine.sky_light.len() { // Bounds check for section index
return Err("Invalid section index".to_string()); let mut light_engine = chunk.light_engine.lock().unwrap();
} if section_index >= light_engine.sky_light.len() {
let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE; return Err("Invalid section index".to_string());
light_engine.sky_light[section_index].set( }
relative.x as usize, let relative_y = (relative.y - chunk.section.min_y) as usize % BlockPalette::SIZE;
relative_y, light_engine.sky_light[section_index].set(
relative.z as usize, relative.x as usize,
light_level, relative_y,
); relative.z as usize,
// Mark chunk as dirty so lighting changes are saved to disk light_level,
if !chunk.is_dirty() { );
chunk.mark_dirty(true); // Mark chunk as dirty so lighting changes are saved to disk
} if !chunk.is_dirty() {
Ok(()) chunk.mark_dirty(true);
}
Ok(())
})
.await
} }
} }

View File

@ -67,7 +67,8 @@ impl<'a> PistonHandler<'a> {
if !self.try_move(self.pos_to, self.motion_direction).await { if !self.try_move(self.pos_to, self.motion_direction).await {
return false; 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; let block = self.world.get_block(&block_pos).await;
if Self::is_block_sticky(block) if Self::is_block_sticky(block)
&& !self.try_move_adjacent_block(block, &block_pos).await && !self.try_move_adjacent_block(block, &block_pos).await

View File

@ -289,10 +289,13 @@ impl RedstoneWireTurbo {
self.current_walk_layer = 1; self.current_walk_layer = 1;
while !self.update_queue[0].is_empty() || !self.update_queue[1].is_empty() { 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 &current_queue {
let block = Block::from_state_id(self.nodes[node_id.index].state.id); let block = Block::from_state_id(self.nodes[node_id.index].state.id);
if block == &Block::REDSTONE_WIRE { 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; .await;
} else { } else {
// This only works because updating any other block than a wire will // 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; self.current_walk_layer += 1;
} }

View File

@ -206,7 +206,7 @@ impl EntityBase for AreaEffectCloudEntity {
#[allow(clippy::semicolon_outside_block)] #[allow(clippy::semicolon_outside_block)]
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
_caller: Arc<dyn EntityBase>, _caller: &'a Arc<dyn EntityBase>,
_server: &'a Server, _server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {

View File

@ -70,12 +70,12 @@ impl NBTStorage for ExperienceOrbEntity {}
impl EntityBase for ExperienceOrbEntity { impl EntityBase for ExperienceOrbEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
let entity = &self.entity; let entity = &self.entity;
entity.tick(caller.clone(), server).await; entity.tick(caller, server).await;
let bounding_box = entity.bounding_box.load(); let bounding_box = entity.bounding_box.load();
let original_velo = entity.velocity.load(); let original_velo = entity.velocity.load();
@ -95,9 +95,9 @@ impl EntityBase for ExperienceOrbEntity {
entity.velocity.store(velo); 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); let age = self.orb_age.fetch_add(1, Ordering::Relaxed);
if age >= 6000 { if age >= 6000 {

View File

@ -50,12 +50,12 @@ impl NBTStorage for FallingEntity {}
impl EntityBase for FallingEntity { impl EntityBase for FallingEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
let entity = &self.entity; let entity = &self.entity;
entity.tick(caller.clone(), server).await; entity.tick(caller, server).await;
let original_velo = entity.velocity.load(); let original_velo = entity.velocity.load();
let mut velo = original_velo; let mut velo = original_velo;
@ -63,8 +63,8 @@ impl EntityBase for FallingEntity {
entity.velocity.store(velo); 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;
if entity.on_ground.load(Ordering::Relaxed) { if entity.on_ground.load(Ordering::Relaxed) {
entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); entity.velocity.store(velo.multiply(0.7, -0.5, 0.7));
entity entity

View File

@ -271,15 +271,15 @@ impl ItemEntity {
Some(tick_move) Some(tick_move)
} }
async fn move_and_apply_friction( async fn move_and_apply_friction<'a>(
&self, &'a self,
caller: &Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &Server, server: &'a Server,
move_velo: Vector3<f64>, move_velo: Vector3<f64>,
) { ) {
let entity = &self.entity; 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; entity.tick_block_collisions(caller, server).await;
let mut friction = 0.98; let mut friction = 0.98;
@ -332,9 +332,9 @@ impl ItemEntity {
true true
} }
async fn sync_motion_if_dirty( async fn sync_motion_if_dirty<'a>(
&self, &'a self,
caller: &Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
original_velo: Vector3<f64>, original_velo: Vector3<f64>,
) { ) {
let entity = &self.entity; let entity = &self.entity;
@ -358,7 +358,7 @@ impl NBTStorage for ItemEntity {}
impl EntityBase for ItemEntity { impl EntityBase for ItemEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -379,12 +379,12 @@ impl EntityBase for ItemEntity {
}; };
if tick_move { if tick_move {
self.move_and_apply_friction(&caller, server, move_velo) self.move_and_apply_friction(caller, server, move_velo)
.await; .await;
} }
if self.process_age_and_merge().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;
} }
}) })
} }

View File

@ -728,7 +728,7 @@ impl LivingEntity {
.await; .await;
} }
async fn tick_movement(&self, server: &Server, caller: Arc<dyn EntityBase>) { async fn tick_movement<'a>(&'a self, server: &'a Server, caller: &'a Arc<dyn EntityBase>) {
if self.jumping_cooldown.load(Relaxed) != 0 { if self.jumping_cooldown.load(Relaxed) != 0 {
self.jumping_cooldown.fetch_sub(1, Relaxed); self.jumping_cooldown.fetch_sub(1, Relaxed);
} }
@ -799,24 +799,24 @@ impl LivingEntity {
&& should_swim_in_fluids && should_swim_in_fluids
&& self.entity.entity_type != &EntityType::STRIDER && self.entity.entity_type != &EntityType::STRIDER
{ {
self.travel_in_fluid(caller.clone(), touching_water).await; self.travel_in_fluid(caller, touching_water).await;
} else { } else {
// TODO: Gliding // 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. // TODO: Apply Soul Speed boot durability when tick_block_underneath is implemented.
//self.entity.tick_block_underneath(&caller); //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 { 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<dyn EntityBase>) { async fn travel_in_air<'a>(&'a self, caller: &'a Arc<dyn EntityBase>) {
// applyMovementInput // applyMovementInput
let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED); let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED);
@ -853,7 +853,7 @@ impl LivingEntity {
self.apply_climbing_speed(); self.apply_climbing_speed();
self.make_move(caller.clone()).await; self.make_move(caller).await;
let mut velo = self.entity.velocity.load(); let mut velo = self.entity.velocity.load();
@ -874,7 +874,7 @@ impl LivingEntity {
if let Some(lev) = levitation { if let Some(lev) = levitation {
velo.y += 0.05f64.mul_add(f64::from(lev.amplifier + 1), -velo.y) * 0.2; velo.y += 0.05f64.mul_add(f64::from(lev.amplifier + 1), -velo.y) * 0.2;
} else { } 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: // TODO: If world is not loaded: replace effective gravity with:
@ -898,11 +898,11 @@ impl LivingEntity {
self.entity.velocity.store(velo); self.entity.velocity.store(velo);
} }
async fn travel_in_fluid(&self, caller: Arc<dyn EntityBase>, water: bool) { async fn travel_in_fluid<'a>(&'a self, caller: &'a Arc<dyn EntityBase>, water: bool) {
let movement_input = self.movement_input.load(); let movement_input = self.movement_input.load();
let falling = self.entity.velocity.load().y <= 0.0; 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); let effective_speed = self.get_attribute_value(&Attributes::MOVEMENT_SPEED);
if water { if water {
@ -995,7 +995,7 @@ impl LivingEntity {
} }
} }
async fn make_move(&self, caller: Arc<dyn EntityBase>) { async fn make_move<'a>(&'a self, caller: &'a Arc<dyn EntityBase>) {
self.entity self.entity
.move_entity(caller, self.entity.velocity.load()) .move_entity(caller, self.entity.velocity.load())
.await; .await;
@ -2028,11 +2028,11 @@ impl EntityBase for LivingEntity {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { 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" // Only tick movement if the entity is alive. This prevents a dead "corpse"
// from continuing to be simulated (accumulating fall_distance/velocity). // from continuing to be simulated (accumulating fall_distance/velocity).
@ -2041,7 +2041,7 @@ impl EntityBase for LivingEntity {
let in_death_animation = let in_death_animation =
self.health.load() <= 0.0 && self.death_time.load(Relaxed) < 20; self.health.load() <= 0.0 && self.death_time.load(Relaxed) < 20;
if is_alive || (in_death_animation && self.entity.entity_type != &EntityType::PLAYER) { 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. // Vanilla-like order: freeze logic runs after movement/collisions.
self.entity.tick_frozen(caller.as_ref()).await; self.entity.tick_frozen(caller.as_ref()).await;
} }

View File

@ -306,11 +306,10 @@ pub trait Mob: EntityBase + Send + Sync {
false false
} }
} }
impl<T: Mob + Send + 'static> EntityBase for T { impl<T: Mob + Send + 'static> EntityBase for T {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -319,11 +318,12 @@ impl<T: Mob + Send + 'static> EntityBase for T {
if mob_entity.breeding_cooldown.load(Relaxed) > 0 { if mob_entity.breeding_cooldown.load(Relaxed) > 0 {
mob_entity.breeding_cooldown.fetch_sub(1, Relaxed); mob_entity.breeding_cooldown.fetch_sub(1, Relaxed);
} }
if mob_entity.love_ticks.load(Relaxed) > 0 { if mob_entity.love_ticks.load(Relaxed) > 0 {
mob_entity.love_ticks.fetch_sub(1, Relaxed); 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) // AI runs before physics (vanilla order: goals → navigator → look → physics)
let age = mob_entity.living_entity.entity.age.load(Relaxed); let age = mob_entity.living_entity.entity.age.load(Relaxed);

View File

@ -117,7 +117,7 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
/// The `server` parameter provides access to the game server instance. /// The `server` parameter provides access to the game server instance.
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -1508,7 +1508,7 @@ impl Entity {
// Move by a delta, adjust for collisions, and send // Move by a delta, adjust for collisions, and send
// Does not send movement. That must be done separately // Does not send movement. That must be done separately
async fn move_entity(&self, caller: Arc<dyn EntityBase>, mut motion: Vector3<f64>) { async fn move_entity<'a>(&'a self, caller: &'a Arc<dyn EntityBase>, mut motion: Vector3<f64>) {
if caller.get_player().is_some() { if caller.get_player().is_some() {
return; return;
} }
@ -2753,7 +2753,7 @@ impl NBTStorage for Entity {
impl EntityBase for Entity { impl EntityBase for Entity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
_server: &'a Server, _server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -2763,9 +2763,9 @@ impl EntityBase for Entity {
.store(was_in_powder_snow, Ordering::Relaxed); .store(was_in_powder_snow, Ordering::Relaxed);
self.is_in_powder_snow.store(false, Ordering::Relaxed); self.is_in_powder_snow.store(false, Ordering::Relaxed);
self.update_last_pos(); self.update_last_pos();
self.tick_portal(&caller).await; self.tick_portal(caller).await;
self.update_fluid_state(&caller).await; self.update_fluid_state(caller).await;
self.check_out_of_world(&*caller).await; self.check_out_of_world(&**caller).await;
let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); let fire_ticks = self.fire_ticks.load(Ordering::Relaxed);
// Check for fire immunity (or if the specific entity is) // Check for fire immunity (or if the specific entity is)
@ -2779,7 +2779,7 @@ impl EntityBase for Entity {
} }
} else { } else {
if fire_ticks % 20 == 0 { 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); self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed);

View File

@ -1597,7 +1597,8 @@ impl Player {
self.last_attacked_ticks.fetch_add(1, Ordering::Relaxed); self.last_attacked_ticks.fetch_add(1, Ordering::Relaxed);
self.living_entity.tick(self.clone(), server).await; let caller: Arc<dyn EntityBase> = self.clone();
self.living_entity.tick(&caller, server).await;
// Vanilla updates pose in PlayerEntity#tick after super.tick(). // Vanilla updates pose in PlayerEntity#tick after super.tick().
self.update_player_pose().await; self.update_player_pose().await;
self.breath_manager.tick(self).await; self.breath_manager.tick(self).await;

View File

@ -191,7 +191,7 @@ impl EntityBase for ArrowEntity {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
_server: &'a Server, _server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {

View File

@ -86,7 +86,7 @@ impl EntityBase for EggEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { self.thrown.process_tick(caller, server).await }) Box::pin(async move { self.thrown.process_tick(caller, server).await })

View File

@ -43,7 +43,7 @@ impl NBTStorage for EnderPearlEntity {}
impl EntityBase for EnderPearlEntity { impl EntityBase for EnderPearlEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { self.thrown.process_tick(caller, server).await }) Box::pin(async move { self.thrown.process_tick(caller, server).await })

View File

@ -123,7 +123,7 @@ impl NBTStorage for EyeOfEnder {}
impl EntityBase for EyeOfEnder { impl EntityBase for EyeOfEnder {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {

View File

@ -103,7 +103,7 @@ impl NBTStorage for FireworkRocketEntity {}
impl EntityBase for FireworkRocketEntity { impl EntityBase for FireworkRocketEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {

View File

@ -82,7 +82,7 @@ impl EntityBase for LingeringPotionEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { self.thrown.process_tick(caller, server).await }) Box::pin(async move { self.thrown.process_tick(caller, server).await })

View File

@ -103,7 +103,7 @@ impl NBTStorage for ThrownItemEntity {}
impl ThrownItemEntity { impl ThrownItemEntity {
/// Process a tick for projectile movement and collisions /// Process a tick for projectile movement and collisions
pub async fn process_tick(&self, caller: Arc<dyn EntityBase>, _server: &Server) { pub async fn process_tick<'a>(&'a self, caller: &'a Arc<dyn EntityBase>, _server: &'a Server) {
let entity = self.get_entity(); let entity = self.get_entity();
let world = entity.world.load(); let world = entity.world.load();

View File

@ -45,7 +45,7 @@ impl NBTStorage for SnowballEntity {}
impl EntityBase for SnowballEntity { impl EntityBase for SnowballEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { self.thrown.process_tick(caller, server).await }) Box::pin(async move { self.thrown.process_tick(caller, server).await })

View File

@ -123,7 +123,7 @@ impl EntityBase for SplashPotionEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { self.thrown.process_tick(caller, server).await }) Box::pin(async move { self.thrown.process_tick(caller, server).await })

View File

@ -75,7 +75,7 @@ impl NBTStorage for WindChargeEntity {}
impl EntityBase for WindChargeEntity { impl EntityBase for WindChargeEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {

View File

@ -36,7 +36,7 @@ impl NBTStorage for TNTEntity {}
impl EntityBase for TNTEntity { impl EntityBase for TNTEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
caller: Arc<dyn EntityBase>, caller: &'a Arc<dyn EntityBase>,
server: &'a Server, server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -46,8 +46,8 @@ impl EntityBase for TNTEntity {
let mut velo = original_velo; let mut velo = original_velo;
velo.y -= self.get_gravity(); velo.y -= self.get_gravity();
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;
entity.velocity.store(velo.multiply(0.98, 0.98, 0.98)); entity.velocity.store(velo.multiply(0.98, 0.98, 0.98));
if entity.on_ground.load(Ordering::Relaxed) { if entity.on_ground.load(Ordering::Relaxed) {
@ -73,7 +73,7 @@ impl EntityBase for TNTEntity {
} else { } else {
// Safe decrement // Safe decrement
self.fuse.store(fuse - 1, Relaxed); self.fuse.store(fuse - 1, Relaxed);
entity.update_fluid_state(&caller).await; entity.update_fluid_state(caller).await;
} }
}) })
} }

View File

@ -104,7 +104,7 @@ impl EntityBase for BoatEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
_caller: Arc<dyn EntityBase>, _caller: &'a Arc<dyn EntityBase>,
_server: &'a Server, _server: &'a Server,
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {

View File

@ -1197,9 +1197,6 @@ impl JavaClient {
return; return;
}; };
let inventory = player.inventory();
let item = inventory.held_item();
let (yaw, pitch) = player.rotation(); let (yaw, pitch) = player.rotation();
let hit_result = player let hit_result = player
.world() .world()
@ -1219,18 +1216,11 @@ impl JavaClient {
PlayerInteractEvent::new( PlayerInteractEvent::new(
player, player,
InteractAction::LeftClickBlock, InteractAction::LeftClickBlock,
&item,
player.world().get_block(&hit_pos).await, player.world().get_block(&hit_pos).await,
Some(hit_pos), Some(hit_pos),
) )
} else { } else {
PlayerInteractEvent::new( PlayerInteractEvent::new(player, InteractAction::LeftClickAir, &Block::AIR, None)
player,
InteractAction::LeftClickAir,
&item,
&Block::AIR,
None,
)
}; };
let server = player.world().server.upgrade().unwrap(); let server = player.world().server.upgrade().unwrap();
@ -2264,18 +2254,11 @@ impl JavaClient {
PlayerInteractEvent::new( PlayerInteractEvent::new(
player, player,
InteractAction::RightClickBlock, InteractAction::RightClickBlock,
&item_in_hand,
player.world().get_block(&hit_pos).await, player.world().get_block(&hit_pos).await,
Some(hit_pos), Some(hit_pos),
) )
} else { } else {
PlayerInteractEvent::new( PlayerInteractEvent::new(player, InteractAction::RightClickAir, &Block::AIR, None)
player,
InteractAction::RightClickAir,
&item_in_hand,
&Block::AIR,
None,
)
}; };
self.prepare_hand_item_for_use(player, hand, &item_in_hand) self.prepare_hand_item_for_use(player, hand, &item_in_hand)
.await; .await;

View File

@ -2,10 +2,8 @@ use std::sync::Arc;
use crate::entity::player::Player; use crate::entity::player::Player;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_macros::{Event, cancellable}; use pumpkin_macros::{Event, cancellable};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex;
use super::PlayerEvent; use super::PlayerEvent;
@ -26,9 +24,6 @@ pub struct PlayerInteractEvent {
/// The position of the block that was clicked, if any. /// The position of the block that was clicked, if any.
pub clicked_pos: Option<BlockPos>, pub clicked_pos: Option<BlockPos>,
/// The item in the player's hand at the time of interaction.
pub item: Arc<Mutex<ItemStack>>,
/// The block that was interacted with. /// The block that was interacted with.
pub block: &'static Block, pub block: &'static Block,
} }
@ -50,14 +45,12 @@ impl PlayerInteractEvent {
pub fn new( pub fn new(
player: &Arc<Player>, player: &Arc<Player>,
action: InteractAction, action: InteractAction,
item: &Arc<Mutex<ItemStack>>,
block: &'static Block, block: &'static Block,
clicked_pos: Option<BlockPos>, clicked_pos: Option<BlockPos>,
) -> Self { ) -> Self {
Self { Self {
player: Arc::clone(player), player: Arc::clone(player),
action, action,
item: Arc::clone(item),
block, block,
clicked_pos, clicked_pos,
cancelled: false, cancelled: false,

View File

@ -576,22 +576,10 @@ impl ToFromWasmEvent for PlayerInteractEvent {
.add_player(self.player.clone()) .add_player(self.player.clone())
.expect("failed to add player resource"); .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 { Event::PlayerInteractEvent(PlayerInteractEventData {
player, player,
action: to_wasm_interact_action(&self.action), action: to_wasm_interact_action(&self.action),
clicked_pos: self.clicked_pos.map(to_wasm_block_position), clicked_pos: self.clicked_pos.map(to_wasm_block_position),
item,
block: self.block.name.to_string(), block: self.block.name.to_string(),
cancelled: self.cancelled, cancelled: self.cancelled,
}) })

View File

@ -300,6 +300,7 @@ impl World {
entity.write_nbt(&mut nbt).await; entity.write_nbt(&mut nbt).await;
if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() { if let Some(old_chunk) = base_entity.first_loaded_chunk_position.load() {
let old_chunk = old_chunk.to_vec2_i32(); let old_chunk = old_chunk.to_vec2_i32();
let chunk = self.level.get_entity_chunk(old_chunk).await; let chunk = self.level.get_entity_chunk(old_chunk).await;
chunk.mark_dirty(true); chunk.mark_dirty(true);
let mut data = chunk.data.lock().await; let mut data = chunk.data.lock().await;
@ -776,7 +777,7 @@ impl World {
for entity in entities_to_tick.iter() { for entity in entities_to_tick.iter() {
entity.get_entity().age.fetch_add(1, Relaxed); entity.get_entity().age.fetch_add(1, Relaxed);
entity.tick(entity.clone(), server).await; entity.tick(entity, server).await;
for player in players.iter() { for player in players.iter() {
if player if player
@ -989,12 +990,14 @@ impl World {
) )
}; };
let spawn_passives = self.level_time.lock().await.time_of_day % 400 == 0; 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> = let spawn_list: Vec<&'static MobCategory> =
natural_spawner::get_filtered_spawning_categories( natural_spawner::get_filtered_spawning_categories(
&spawn_state, &spawn_state,
spawn_mobs, spawn_mobs,
peaceful && spawn_mobs && spawn_monsters, spawn_enemies,
peaceful && spawn_mobs && spawn_passives && spawn_monsters, spawn_passives,
); );
// log::debug!("spawning list size {}", spawn_list.len()); // 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. /// Gets the `MOTION_BLOCKING` heightmap value for a given XZ position.
pub async fn get_motion_blocking_height(&self, x: i32, z: i32) -> i32 { pub async fn get_motion_blocking_height(&self, x: i32, z: i32) -> i32 {
let chunk_pos = Vector2::new(x >> 4, z >> 4); let chunk_pos = Vector2::new(x >> 4, z >> 4);
let chunk = self.level.get_chunk(chunk_pos).await; self.level
chunk .get_or_fetch_chunk(chunk_pos, |chunk| {
.heightmap chunk
.lock() .heightmap
.unwrap() .lock()
.get(MotionBlocking, x, z, self.min_y) .unwrap()
.get(MotionBlocking, x, z, self.min_y)
})
.await
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
@ -2388,7 +2394,10 @@ impl World {
// Ensure at least the center chunk is sent synchronously before teleport. // Ensure at least the center chunk is sent synchronously before teleport.
if let crate::net::ClientPlatform::Java(java_client) = &player.client { if let crate::net::ClientPlatform::Java(java_client) = &player.client {
let center_chunk = player.living_entity.entity.chunk_pos.load(); 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(&CChunkBatchStart).await;
java_client.send_packet_now(&CChunkData(&chunk)).await; java_client.send_packet_now(&CChunkData(&chunk)).await;
java_client java_client
@ -2520,14 +2529,18 @@ impl World {
let mut data = chunk.data.lock().await; let mut data = chunk.data.lock().await;
if old_chunk == current_chunk_coordinate { if old_chunk == current_chunk_coordinate {
data.insert(base_entity.entity_uuid, nbt); data.insert(base_entity.entity_uuid, nbt.clone());
return; return;
} }
// The chunk has changed, lets remove the entity from the old chunk // The chunk has changed, lets remove the entity from the old chunk
data.remove(&base_entity.entity_uuid); 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); chunk.mark_dirty(true);
} }
@ -3000,13 +3013,12 @@ impl World {
pub async fn add_entity_silent(&self, entity: Arc<dyn EntityBase>) { pub async fn add_entity_silent(&self, entity: Arc<dyn EntityBase>) {
let base_entity = entity.get_entity(); let base_entity = entity.get_entity();
let chunk_coordinate = base_entity.block_pos.load().chunk_position(); 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 chunk = self.level.get_entity_chunk(chunk_coordinate).await;
{ chunk.data.lock().await.insert(base_entity.entity_uuid, nbt);
let mut nbt = PNbtCompound::new(); chunk.mark_dirty(true);
entity.write_nbt(&mut nbt).await;
chunk.data.lock().await.insert(base_entity.entity_uuid, nbt);
chunk.mark_dirty(true);
};
self.entities.rcu(|current_entities| { self.entities.rcu(|current_entities| {
let mut new_entities = (**current_entities).clone(); let mut new_entities = (**current_entities).clone();
@ -3048,23 +3060,26 @@ impl World {
flags: BlockFlags, flags: BlockFlags,
) -> BlockStateId { ) -> BlockStateId {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let level = &self.level; let replaced_block_state_id = self
let chunk = level.get_chunk(chunk_coordinate).await; .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 { if replaced_block_state_id == block_state_id {
return 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 self.unsent_block_changes
.lock() .lock()
@ -3172,9 +3187,9 @@ impl World {
let (_chunk_coordinate, _) = position.chunk_and_chunk_relative_position(); let (_chunk_coordinate, _) = position.chunk_and_chunk_relative_position();
level self.level
.light_engine .light_engine
.update_lighting_at(level, *position) .update_lighting_at(&self.level, *position)
.await; .await;
replaced_block_state_id replaced_block_state_id
@ -3660,19 +3675,21 @@ impl World {
} }
pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>> { pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>> {
let chunk = self.level.get_chunk(block_pos.chunk_position()).await; self.level
chunk.block_entities.lock().unwrap().get(block_pos).cloned() .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<dyn BlockEntity>) { pub async fn add_block_entity(&self, block_entity: Arc<dyn BlockEntity>) {
let block_pos = block_entity.get_position(); 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(); let block_entity_nbt = block_entity.chunk_data_nbt();
if let Some(nbt) = &block_entity_nbt { if let Some(nbt) = &block_entity_nbt {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
to_bytes_unnamed(nbt, &mut bytes).unwrap(); to_bytes_unnamed(nbt, &mut bytes).unwrap();
let chunk_pos = block_pos.chunk_position();
self.broadcast_to_chunk( self.broadcast_to_chunk(
chunk_pos, chunk_pos,
&CBlockEntityData::new( &CBlockEntityData::new(
@ -3684,36 +3701,42 @@ impl World {
.await; .await;
} }
chunk self.level
.block_entities .get_or_fetch_chunk(chunk_pos, |chunk| {
.lock() chunk
.unwrap() .block_entities
.insert(block_pos, block_entity); .lock()
chunk.mark_dirty(true); .unwrap()
.insert(block_pos, block_entity.clone());
chunk.mark_dirty(true);
})
.await;
} }
pub async fn remove_block_entity(&self, block_pos: &BlockPos) { pub async fn remove_block_entity(&self, block_pos: &BlockPos) {
let chunk = self.level.get_chunk(block_pos.chunk_position()).await; self.level
if chunk .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| {
.block_entities if chunk
.lock() .block_entities
.unwrap() .lock()
.remove(block_pos) .unwrap()
.is_some() .remove(block_pos)
{ .is_some()
chunk.mark_dirty(true); {
} chunk.mark_dirty(true);
}
})
.await;
} }
pub async fn update_block_entity(&self, block_entity: &Arc<dyn BlockEntity>) { pub async fn update_block_entity(&self, block_entity: &Arc<dyn BlockEntity>) {
let block_pos = block_entity.get_position(); 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(); let block_entity_nbt = block_entity.chunk_data_nbt();
if let Some(nbt) = &block_entity_nbt { if let Some(nbt) = &block_entity_nbt {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
to_bytes_unnamed(nbt, &mut bytes).unwrap(); to_bytes_unnamed(nbt, &mut bytes).unwrap();
let chunk_pos = block_pos.chunk_position();
self.broadcast_to_chunk( self.broadcast_to_chunk(
chunk_pos, chunk_pos,
&CBlockEntityData::new( &CBlockEntityData::new(
@ -3724,7 +3747,11 @@ impl World {
) )
.await; .await;
} }
chunk.mark_dirty(true); self.level
.get_or_fetch_chunk(chunk_pos, |chunk| {
chunk.mark_dirty(true);
})
.await;
} }
fn intersects_aabb_with_direction( fn intersects_aabb_with_direction(

View File

@ -434,30 +434,28 @@ pub async fn spawn_category_for_position(
prepared_data.push((base_entity.entity_uuid, nbt, packet, entity.clone())); prepared_data.push((base_entity.entity_uuid, nbt, packet, entity.clone()));
} }
{ let chunk_handle = world.level.get_entity_chunk(*chunk_pos).await;
let chunk_handle = world.level.get_entity_chunk(*chunk_pos).await; let mut data = chunk_handle.data.lock().await;
let mut data = chunk_handle.data.lock().await;
for (uuid, nbt, _, _) in &prepared_data { for (uuid, nbt, _, _) in &prepared_data {
data.insert(*uuid, nbt.clone()); data.insert(*uuid, nbt.clone());
} }
drop(data); drop(data);
chunk_handle.mark_dirty(true); chunk_handle.mark_dirty(true);
world.entities.rcu(|current_entities| { world.entities.rcu(|current_entities| {
let mut new_entities = (**current_entities).clone(); let mut new_entities = (**current_entities).clone();
for (uuid, _, _, entity_ref) in &prepared_data { for (uuid, _, _, entity_ref) in &prepared_data {
if !new_entities if !new_entities
.iter() .iter()
.any(|e| e.get_entity().entity_uuid == *uuid) .any(|e| e.get_entity().entity_uuid == *uuid)
{ {
new_entities.push(entity_ref.clone()); new_entities.push(entity_ref.clone());
}
} }
new_entities }
}); new_entities
}; });
for (_, _, packet, _) in prepared_data { for (_, _, packet, _) in prepared_data {
world.broadcast_packet_all(&packet).await; world.broadcast_packet_all(&packet).await;